aboutsummaryrefslogtreecommitdiff
path: root/src/net_processing.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/net_processing.cpp')
-rw-r--r--src/net_processing.cpp99
1 files changed, 97 insertions, 2 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 59df5d6502..3aa13fff43 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -23,6 +23,7 @@
#include "primitives/transaction.h"
#include "random.h"
#include "reverse_iterator.h"
+#include "scheduler.h"
#include "tinyformat.h"
#include "txmempool.h"
#include "ui_interface.h"
@@ -127,7 +128,6 @@ namespace {
/** Number of outbound peers with m_chain_sync.m_protect. */
int g_outbound_peers_with_protect_from_disconnect = 0;
-
/** When our tip was last updated. */
int64_t g_last_tip_update = 0;
@@ -435,6 +435,15 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) {
}
}
+bool TipMayBeStale(const Consensus::Params &consensusParams)
+{
+ AssertLockHeld(cs_main);
+ if (g_last_tip_update == 0) {
+ g_last_tip_update = GetTime();
+ }
+ return g_last_tip_update < GetTime() - consensusParams.nPowTargetSpacing * 3 && mapBlocksInFlight.empty();
+}
+
// Requires cs_main
bool CanDirectFetch(const Consensus::Params &consensusParams)
{
@@ -772,9 +781,17 @@ static bool StaleBlockRequestAllowed(const CBlockIndex* pindex, const Consensus:
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
}
-PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) {
+PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler) : connman(connmanIn), m_stale_tip_check_time(0) {
// Initialize global variables that cannot be constructed at startup.
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
+
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ // Stale tip checking and peer eviction are on two different timers, but we
+ // don't want them to get out of sync due to drift in the scheduler, so we
+ // combine them in one function and schedule at the quicker (peer-eviction)
+ // timer.
+ static_assert(EXTRA_PEER_CHECK_INTERVAL < STALE_CHECK_INTERVAL, "peer eviction timer should be less than stale tip check timer");
+ scheduler.scheduleEvery(std::bind(&PeerLogicValidation::CheckForStaleTipAndEvictPeers, this, consensusParams), EXTRA_PEER_CHECK_INTERVAL * 1000);
}
void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex, const std::vector<CTransactionRef>& vtxConflicted) {
@@ -1424,6 +1441,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
// If this is an outbound peer, check to see if we should protect
// it from the bad/lagging chain logic.
if (g_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainWork >= chainActive.Tip()->nChainWork && !nodestate->m_chain_sync.m_protect) {
+ LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom->GetId());
nodestate->m_chain_sync.m_protect = true;
++g_outbound_peers_with_protect_from_disconnect;
}
@@ -3004,6 +3022,83 @@ void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
}
}
+void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
+{
+ // Check whether we have too many outbound peers
+ int extra_peers = connman->GetExtraOutboundCount();
+ if (extra_peers > 0) {
+ // If we have more outbound peers than we target, disconnect one.
+ // Pick the outbound peer that least recently announced
+ // us a new block, with ties broken by choosing the more recent
+ // connection (higher node id)
+ NodeId worst_peer = -1;
+ int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
+
+ LOCK(cs_main);
+
+ connman->ForEachNode([&](CNode* pnode) {
+ // Ignore non-outbound peers, or nodes marked for disconnect already
+ if (!IsOutboundDisconnectionCandidate(pnode) || pnode->fDisconnect) return;
+ CNodeState *state = State(pnode->GetId());
+ if (state == nullptr) return; // shouldn't be possible, but just in case
+ // Don't evict our protected peers
+ if (state->m_chain_sync.m_protect) return;
+ if (state->m_last_block_announcement < oldest_block_announcement || (state->m_last_block_announcement == oldest_block_announcement && pnode->GetId() > worst_peer)) {
+ worst_peer = pnode->GetId();
+ oldest_block_announcement = state->m_last_block_announcement;
+ }
+ });
+ if (worst_peer != -1) {
+ bool disconnected = connman->ForNode(worst_peer, [&](CNode *pnode) {
+ // Only disconnect a peer that has been connected to us for
+ // some reasonable fraction of our check-frequency, to give
+ // it time for new information to have arrived.
+ // Also don't disconnect any peer we're trying to download a
+ // block from.
+ CNodeState &state = *State(pnode->GetId());
+ if (time_in_seconds - pnode->nTimeConnected > MINIMUM_CONNECT_TIME && state.nBlocksInFlight == 0) {
+ LogPrint(BCLog::NET, "disconnecting extra outbound peer=%d (last block announcement received at time %d)\n", pnode->GetId(), oldest_block_announcement);
+ pnode->fDisconnect = true;
+ return true;
+ } else {
+ LogPrint(BCLog::NET, "keeping outbound peer=%d chosen for eviction (connect time: %d, blocks_in_flight: %d)\n", pnode->GetId(), pnode->nTimeConnected, state.nBlocksInFlight);
+ return false;
+ }
+ });
+ if (disconnected) {
+ // If we disconnected an extra peer, that means we successfully
+ // connected to at least one peer after the last time we
+ // detected a stale tip. Don't try any more extra peers until
+ // we next detect a stale tip, to limit the load we put on the
+ // network from these extra connections.
+ connman->SetTryNewOutboundPeer(false);
+ }
+ }
+ }
+}
+
+void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams)
+{
+ if (connman == nullptr) return;
+
+ int64_t time_in_seconds = GetTime();
+
+ EvictExtraOutboundPeers(time_in_seconds);
+
+ if (time_in_seconds > m_stale_tip_check_time) {
+ LOCK(cs_main);
+ // Check whether our tip is stale, and if so, allow using an extra
+ // outbound peer
+ if (TipMayBeStale(consensusParams)) {
+ LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - g_last_tip_update);
+ connman->SetTryNewOutboundPeer(true);
+ } else if (connman->GetTryNewOutboundPeer()) {
+ connman->SetTryNewOutboundPeer(false);
+ }
+ m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL;
+ }
+}
+
class CompareInvMempoolOrder
{
CTxMemPool *mp;