Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/processor.cpp
| // Copyright (c) 2018-2019 The Bitcoin developers | // Copyright (c) 2018-2019 The Bitcoin developers | ||||
| // Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
| // file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
| #include <avalanche/processor.h> | #include <avalanche/processor.h> | ||||
| #include <avalanche/avalanche.h> | |||||
| #include <avalanche/delegationbuilder.h> | #include <avalanche/delegationbuilder.h> | ||||
| #include <avalanche/peermanager.h> | #include <avalanche/peermanager.h> | ||||
| #include <avalanche/validation.h> | #include <avalanche/validation.h> | ||||
| #include <avalanche/voterecord.h> | #include <avalanche/voterecord.h> | ||||
| #include <chain.h> | #include <chain.h> | ||||
| #include <key_io.h> // For DecodeSecret | #include <key_io.h> // For DecodeSecret | ||||
| #include <net.h> | #include <net.h> | ||||
| #include <netmessagemaker.h> | #include <netmessagemaker.h> | ||||
| #include <reverse_iterator.h> | #include <reverse_iterator.h> | ||||
| #include <scheduler.h> | #include <scheduler.h> | ||||
| #include <util/bitmanip.h> | #include <util/bitmanip.h> | ||||
| #include <util/moneystr.h> | |||||
| #include <util/translation.h> | #include <util/translation.h> | ||||
| #include <validation.h> | #include <validation.h> | ||||
| #include <chrono> | #include <chrono> | ||||
| #include <tuple> | #include <tuple> | ||||
| /** | /** | ||||
| * Run the avalanche event loop every 10ms. | * Run the avalanche event loop every 10ms. | ||||
| ▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | void updatedBlockTip() override { | ||||
| } | } | ||||
| m_processor->peerManager->updatedBlockTip(); | m_processor->peerManager->updatedBlockTip(); | ||||
| } | } | ||||
| }; | }; | ||||
| Processor::Processor(const ArgsManager &argsman, interfaces::Chain &chain, | Processor::Processor(const ArgsManager &argsman, interfaces::Chain &chain, | ||||
| CConnman *connmanIn, std::unique_ptr<PeerData> peerDataIn, | CConnman *connmanIn, std::unique_ptr<PeerData> peerDataIn, | ||||
| CKey sessionKeyIn) | CKey sessionKeyIn, uint32_t minQuorumPeerScoreIn, | ||||
| double minQuorumNodeScoreRatioIn) | |||||
| : connman(connmanIn), | : connman(connmanIn), | ||||
| queryTimeoutDuration(argsman.GetArg( | queryTimeoutDuration(argsman.GetArg( | ||||
| "-avatimeout", AVALANCHE_DEFAULT_QUERY_TIMEOUT.count())), | "-avatimeout", AVALANCHE_DEFAULT_QUERY_TIMEOUT.count())), | ||||
| round(0), peerManager(std::make_unique<PeerManager>()), | round(0), peerManager(std::make_unique<PeerManager>()), | ||||
| peerData(std::move(peerDataIn)), sessionKey(std::move(sessionKeyIn)) { | peerData(std::move(peerDataIn)), sessionKey(std::move(sessionKeyIn)), | ||||
| minQuorumScore(minQuorumPeerScoreIn), | |||||
| minQuorumConnectedScoreRatio(minQuorumNodeScoreRatioIn), | |||||
| quorumAvailable(false), | |||||
| quorumCheckTimeoutDuration(quorumCheckInterval(argsman)) { | |||||
| // Make sure we get notified of chain state changes. | // Make sure we get notified of chain state changes. | ||||
| chainNotificationsHandler = | chainNotificationsHandler = | ||||
| chain.handleNotifications(std::make_shared<NotificationsHandler>(this)); | chain.handleNotifications(std::make_shared<NotificationsHandler>(this)); | ||||
| } | } | ||||
| Processor::~Processor() { | Processor::~Processor() { | ||||
| chainNotificationsHandler.reset(); | chainNotificationsHandler.reset(); | ||||
| stopEventLoop(); | stopEventLoop(); | ||||
| ▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | if (argsman.IsArgSet("-avaproof")) { | ||||
| peerData->delegation = dgb->build(); | peerData->delegation = dgb->build(); | ||||
| if (!VerifyDelegation(peerData->delegation, sessionPubKey, error)) { | if (!VerifyDelegation(peerData->delegation, sessionPubKey, error)) { | ||||
| // error is set by VerifyDelegation | // error is set by VerifyDelegation | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| } | } | ||||
| // Determine quorum parameters | |||||
| Amount minQuorumStake = AVALANCHE_DEFAULT_MIN_QUORUM_STAKE; | |||||
| if (gArgs.IsArgSet("-avaminquorumstake") && | |||||
| !ParseMoney(gArgs.GetArg("-avaminquorumstake", ""), minQuorumStake)) { | |||||
| error = _("The avalanche min quorum stake amount is invalid."); | |||||
| return nullptr; | |||||
| } | |||||
| double minQuorumConnectedStakeRatio = | |||||
| AVALANCHE_DEFAULT_MIN_QUORUM_CONNECTED_STAKE_RATIO; | |||||
| if (gArgs.IsArgSet("-avaminquorumconnectedstakeratio") && | |||||
| !ParseDouble(gArgs.GetArg("-avaminquorumconnectedstakeratio", ""), | |||||
| &minQuorumConnectedStakeRatio)) { | |||||
| error = _("The avalanche min quorum connected stake ratio is invalid."); | |||||
| return nullptr; | |||||
| } | |||||
| // We can't use std::make_unique with a private constructor | // We can't use std::make_unique with a private constructor | ||||
| return std::unique_ptr<Processor>(new Processor( | return std::unique_ptr<Processor>(new Processor( | ||||
| argsman, chain, connman, std::move(peerData), std::move(sessionKey))); | argsman, chain, connman, std::move(peerData), std::move(sessionKey), | ||||
| Proof::amountToScore(minQuorumStake), minQuorumConnectedStakeRatio)); | |||||
| } | } | ||||
| bool Processor::addBlockToReconcile(const CBlockIndex *pindex) { | bool Processor::addBlockToReconcile(const CBlockIndex *pindex) { | ||||
| bool isAccepted; | bool isAccepted; | ||||
| { | { | ||||
| LOCK(cs_main); | LOCK(cs_main); | ||||
| if (!IsWorthPolling(pindex)) { | if (!IsWorthPolling(pindex)) { | ||||
| ▲ Show 20 Lines • Show All 405 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| void Processor::runEventLoop() { | void Processor::runEventLoop() { | ||||
| // Don't do Avalanche while node is IBD'ing | // Don't do Avalanche while node is IBD'ing | ||||
| if (::ChainstateActive().IsInitialBlockDownload()) { | if (::ChainstateActive().IsInitialBlockDownload()) { | ||||
| return; | return; | ||||
| } | } | ||||
| // Don't poll if quorum hasn't been established yet | |||||
| auto now = std::chrono::steady_clock::now(); | |||||
| if (now > nextQuorumCheckAt) { | |||||
| quorumAvailable = isQuorumAvailable(); | |||||
Fabien: This implies that we can lose the ability to poll if a node disconnects for example. Is that… | |||||
| nextQuorumCheckAt = now + quorumCheckTimeoutDuration; | |||||
| } | |||||
| if (!quorumAvailable) { | |||||
| return; | |||||
| } | |||||
| // First things first, check if we have requests that timed out and clear | // First things first, check if we have requests that timed out and clear | ||||
| // them. | // them. | ||||
| clearTimedoutRequests(); | clearTimedoutRequests(); | ||||
| // Make sure there is at least one suitable node to query before gathering | // Make sure there is at least one suitable node to query before gathering | ||||
| // invs. | // invs. | ||||
| NodeId nodeid = getSuitableNodeToQuery(); | NodeId nodeid = getSuitableNodeToQuery(); | ||||
| if (nodeid == NO_NODE) { | if (nodeid == NO_NODE) { | ||||
| ▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | do { | ||||
| peerManager->removeNode(nodeid); | peerManager->removeNode(nodeid); | ||||
| } | } | ||||
| // Get next suitable node to try again | // Get next suitable node to try again | ||||
| nodeid = getSuitableNodeToQuery(); | nodeid = getSuitableNodeToQuery(); | ||||
| } while (nodeid != NO_NODE); | } while (nodeid != NO_NODE); | ||||
| } | } | ||||
| /* | |||||
| * Returns a bool indicating whether we have a usable Avalanche quorum enabling | |||||
| * us to take decisions based on polls. | |||||
| */ | |||||
| bool Processor::isQuorumAvailable() { | |||||
| // Sum the score of all peers and, separately, the stake of peers with nodes | |||||
| uint32_t totalScore = 0; | |||||
| uint32_t connectedScore = 0; | |||||
| { | |||||
| LOCK(cs_peerManager); | |||||
| peerManager->forEachPeer([&totalScore, &connectedScore](Peer peer) { | |||||
| totalScore += peer.getScore(); | |||||
| if (peer.node_count > 0) { | |||||
| connectedScore += peer.getScore(); | |||||
| } | |||||
| }); | |||||
FabienUnsubmitted Not Done Inline ActionsThis is a hint that this calculation needs to happen in the peer manager since it doesn't involve the processor at all Fabien: This is a hint that this calculation needs to happen in the peer manager since it doesn't… | |||||
| } | |||||
| // Ensure enough is being staked overall | |||||
| if (totalScore < minQuorumScore) { | |||||
| LogPrint(BCLog::AVALANCHE, | |||||
| "Quorum unavailable: Total score is %d; need at least %d\n", | |||||
| totalScore, minQuorumScore); | |||||
| return false; | |||||
| } | |||||
| // Ensure we have nodes for enough of the overall stake | |||||
| uint32_t minConnectedScore = | |||||
| std::round(double(totalScore) * minQuorumConnectedScoreRatio); | |||||
| if (connectedScore < minConnectedScore) { | |||||
| LogPrint( | |||||
| BCLog::AVALANCHE, | |||||
| "Quorum unavailable: Connected score is %d; need at least %d\n", | |||||
| connectedScore, minConnectedScore); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| } // namespace avalanche | } // namespace avalanche | ||||
This implies that we can lose the ability to poll if a node disconnects for example. Is that the intended behavior ? It seems to me that latching the flag makes more sense