|
|
@@ -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" |
|
|
@@ -119,7 +120,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; |
|
|
|
|
|
|
@@ -428,6 +428,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) |
|
|
|
{ |
|
|
@@ -754,9 +763,17 @@ void Misbehaving(NodeId pnode, int howmuch) |
|
|
|
// blockchain -> download logic notification |
|
|
|
// |
|
|
|
|
|
|
|
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) { |
|
|
@@ -1412,6 +1429,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; |
|
|
|
} |
|
|
@@ -2978,6 +2996,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; |