Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
Show First 20 Lines • Show All 553 Lines • ▼ Show 20 Lines | private: | ||||
//! Next time to check for stale tip | //! Next time to check for stale tip | ||||
int64_t m_stale_tip_check_time; | int64_t m_stale_tip_check_time; | ||||
/** Whether this node is running in blocks only mode */ | /** Whether this node is running in blocks only mode */ | ||||
const bool m_ignore_incoming_txs; | const bool m_ignore_incoming_txs; | ||||
/** | /** | ||||
* Whether we've completed initial sync yet, for determining when to turn | |||||
* on extra block-relay-only peers. | |||||
*/ | |||||
bool m_initial_sync_finished{false}; | |||||
/** | |||||
* Protects m_peer_map. This mutex must not be locked while holding a lock | * Protects m_peer_map. This mutex must not be locked while holding a lock | ||||
* on any of the mutexes inside a Peer object. | * on any of the mutexes inside a Peer object. | ||||
*/ | */ | ||||
mutable Mutex m_peer_mutex; | mutable Mutex m_peer_mutex; | ||||
/** | /** | ||||
* Map of all Peer objects, keyed by peer id. This map is protected | * Map of all Peer objects, keyed by peer id. This map is protected | ||||
* by the m_peer_mutex. Once a shared pointer reference is | * by the m_peer_mutex. Once a shared pointer reference is | ||||
* taken, the lock may be released. Individual fields are protected by | * taken, the lock may be released. Individual fields are protected by | ||||
▲ Show 20 Lines • Show All 2,892 Lines • ▼ Show 20 Lines | if (msg_type == NetMsgType::VERACK) { | ||||
if (!pfrom.IsInboundConn()) { | if (!pfrom.IsInboundConn()) { | ||||
LogPrintf( | LogPrintf( | ||||
"New outbound peer connected: version: %d, blocks=%d, " | "New outbound peer connected: version: %d, blocks=%d, " | ||||
"peer=%d%s (%s)\n", | "peer=%d%s (%s)\n", | ||||
pfrom.nVersion.load(), peer->m_starting_height, pfrom.GetId(), | pfrom.nVersion.load(), peer->m_starting_height, pfrom.GetId(), | ||||
(fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToString()) | (fLogIPs ? strprintf(", peeraddr=%s", pfrom.addr.ToString()) | ||||
: ""), | : ""), | ||||
pfrom.m_tx_relay == nullptr ? "block-relay" : "full-relay"); | pfrom.IsBlockOnlyConn() ? "block-relay" : "full-relay"); | ||||
} | } | ||||
if (pfrom.GetCommonVersion() >= SENDHEADERS_VERSION) { | if (pfrom.GetCommonVersion() >= SENDHEADERS_VERSION) { | ||||
// Tell our peer we prefer to receive headers rather than inv's | // Tell our peer we prefer to receive headers rather than inv's | ||||
// We send this to non-NODE NETWORK peers as well, because even | // We send this to non-NODE NETWORK peers as well, because even | ||||
// non-NODE NETWORK peers can announce blocks (such as pruning | // non-NODE NETWORK peers can announce blocks (such as pruning | ||||
// nodes) | // nodes) | ||||
m_connman.PushMessage(&pfrom, | m_connman.PushMessage(&pfrom, | ||||
▲ Show 20 Lines • Show All 1,897 Lines • ▼ Show 20 Lines | if (!state.m_chain_sync.m_protect && pto.IsOutboundOrBlockRelayConn() && | ||||
state.m_chain_sync.m_timeout = | state.m_chain_sync.m_timeout = | ||||
time_in_seconds + HEADERS_RESPONSE_TIME; | time_in_seconds + HEADERS_RESPONSE_TIME; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void PeerManagerImpl::EvictExtraOutboundPeers(int64_t time_in_seconds) { | void PeerManagerImpl::EvictExtraOutboundPeers(int64_t time_in_seconds) { | ||||
// Check whether we have too many outbound peers | // If we have any extra block-relay-only peers, disconnect the youngest | ||||
int extra_peers = m_connman.GetExtraOutboundCount(); | // unless it's given us a block -- in which case, compare with the | ||||
if (extra_peers <= 0) { | // second-youngest, and out of those two, disconnect the peer who least | ||||
// recently gave us a block. | |||||
// The youngest block-relay-only peer would be the extra peer we connected | |||||
// to temporarily in order to sync our tip; see net.cpp. | |||||
// Note that we use higher nodeid as a measure for most recent connection. | |||||
if (m_connman.GetExtraBlockRelayCount() > 0) { | |||||
std::pair<NodeId, int64_t> youngest_peer{-1, 0}, | |||||
next_youngest_peer{-1, 0}; | |||||
m_connman.ForEachNode([&](CNode *pnode) { | |||||
if (!pnode->IsBlockOnlyConn() || pnode->fDisconnect) { | |||||
return; | |||||
} | |||||
if (pnode->GetId() > youngest_peer.first) { | |||||
next_youngest_peer = youngest_peer; | |||||
youngest_peer.first = pnode->GetId(); | |||||
youngest_peer.second = pnode->nLastBlockTime; | |||||
} | |||||
}); | |||||
NodeId to_disconnect = youngest_peer.first; | |||||
if (youngest_peer.second > next_youngest_peer.second) { | |||||
// Our newest block-relay-only peer gave us a block more recently; | |||||
// disconnect our second youngest. | |||||
to_disconnect = next_youngest_peer.first; | |||||
} | |||||
m_connman.ForNode( | |||||
to_disconnect, | |||||
[&](CNode *pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { | |||||
AssertLockHeld(::cs_main); | |||||
// Make sure we're not getting a block right now, and that we've | |||||
// been connected long enough for this eviction to happen at | |||||
// all. Note that we only request blocks from a peer if we learn | |||||
// of a valid headers chain with at least as much work as our | |||||
// tip. | |||||
CNodeState *node_state = State(pnode->GetId()); | |||||
if (node_state == nullptr || | |||||
(time_in_seconds - pnode->nTimeConnected >= | |||||
MINIMUM_CONNECT_TIME && | |||||
node_state->nBlocksInFlight == 0)) { | |||||
pnode->fDisconnect = true; | |||||
LogPrint(BCLog::NET, | |||||
"disconnecting extra block-relay-only peer=%d " | |||||
"(last block received at time %d)\n", | |||||
pnode->GetId(), pnode->nLastBlockTime); | |||||
return true; | |||||
} else { | |||||
LogPrint( | |||||
BCLog::NET, | |||||
"keeping block-relay-only peer=%d chosen for eviction " | |||||
"(connect time: %d, blocks_in_flight: %d)\n", | |||||
pnode->GetId(), pnode->nTimeConnected, | |||||
node_state->nBlocksInFlight); | |||||
} | |||||
return false; | |||||
}); | |||||
} | |||||
// Check whether we have too many OUTBOUND_FULL_RELAY peers | |||||
if (m_connman.GetExtraFullOutboundCount() <= 0) { | |||||
return; | return; | ||||
} | } | ||||
// If we have more outbound peers than we target, disconnect one. | // If we have more OUTBOUND_FULL_RELAY peers than we target, disconnect one. | ||||
// Pick the outbound peer that least recently announced us a new block, with | // Pick the OUTBOUND_FULL_RELAY peer that least recently announced us a new | ||||
// ties broken by choosing the more recent connection (higher node id) | // block, with ties broken by choosing the more recent connection (higher | ||||
// node id) | |||||
NodeId worst_peer = -1; | NodeId worst_peer = -1; | ||||
int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max(); | int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max(); | ||||
m_connman.ForEachNode([&](CNode *pnode) EXCLUSIVE_LOCKS_REQUIRED( | m_connman.ForEachNode([&](CNode *pnode) EXCLUSIVE_LOCKS_REQUIRED( | ||||
::cs_main) { | ::cs_main) { | ||||
AssertLockHeld(::cs_main); | AssertLockHeld(::cs_main); | ||||
// Ignore non-outbound peers, or nodes marked for disconnect already | // Only consider OUTBOUND_FULL_RELAY peers that are not already marked | ||||
if (!pnode->IsOutboundOrBlockRelayConn() || pnode->fDisconnect) { | // for disconnection | ||||
if (!pnode->IsFullOutboundConn() || pnode->fDisconnect) { | |||||
return; | return; | ||||
} | } | ||||
CNodeState *state = State(pnode->GetId()); | CNodeState *state = State(pnode->GetId()); | ||||
if (state == nullptr) { | if (state == nullptr) { | ||||
// shouldn't be possible, but just in case | // shouldn't be possible, but just in case | ||||
return; | return; | ||||
} | } | ||||
// Don't evict our protected peers | // Don't evict our protected peers | ||||
if (state->m_chain_sync.m_protect) { | if (state->m_chain_sync.m_protect) { | ||||
return; | return; | ||||
} | } | ||||
// Don't evict our block-relay-only peers. | |||||
if (pnode->m_tx_relay == nullptr) { | |||||
return; | |||||
} | |||||
if (state->m_last_block_announcement < oldest_block_announcement || | if (state->m_last_block_announcement < oldest_block_announcement || | ||||
(state->m_last_block_announcement == oldest_block_announcement && | (state->m_last_block_announcement == oldest_block_announcement && | ||||
pnode->GetId() > worst_peer)) { | pnode->GetId() > worst_peer)) { | ||||
worst_peer = pnode->GetId(); | worst_peer = pnode->GetId(); | ||||
oldest_block_announcement = state->m_last_block_announcement; | oldest_block_announcement = state->m_last_block_announcement; | ||||
} | } | ||||
}); | }); | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
void PeerManagerImpl::CheckForStaleTipAndEvictPeers() { | void PeerManagerImpl::CheckForStaleTipAndEvictPeers() { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
int64_t time_in_seconds = GetTime(); | int64_t time_in_seconds = GetTime(); | ||||
EvictExtraOutboundPeers(time_in_seconds); | EvictExtraOutboundPeers(time_in_seconds); | ||||
if (time_in_seconds <= m_stale_tip_check_time) { | if (time_in_seconds > m_stale_tip_check_time) { | ||||
return; | // Check whether our tip is stale, and if so, allow using an extra | ||||
} | // outbound peer. | ||||
// Check whether our tip is stale, and if so, allow using an extra outbound | |||||
// peer. | |||||
if (!fImporting && !fReindex && m_connman.GetNetworkActive() && | if (!fImporting && !fReindex && m_connman.GetNetworkActive() && | ||||
m_connman.GetUseAddrmanOutgoing() && | m_connman.GetUseAddrmanOutgoing() && | ||||
TipMayBeStale(m_chainparams.GetConsensus())) { | TipMayBeStale(m_chainparams.GetConsensus())) { | ||||
LogPrintf("Potential stale tip detected, will try using extra outbound " | LogPrintf("Potential stale tip detected, will try using extra " | ||||
"peer (last tip update: %d seconds ago)\n", | "outbound peer (last tip update: %d seconds ago)\n", | ||||
time_in_seconds - g_last_tip_update); | time_in_seconds - g_last_tip_update); | ||||
m_connman.SetTryNewOutboundPeer(true); | m_connman.SetTryNewOutboundPeer(true); | ||||
} else if (m_connman.GetTryNewOutboundPeer()) { | } else if (m_connman.GetTryNewOutboundPeer()) { | ||||
m_connman.SetTryNewOutboundPeer(false); | m_connman.SetTryNewOutboundPeer(false); | ||||
} | } | ||||
m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL; | m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL; | ||||
} | } | ||||
if (!m_initial_sync_finished && | |||||
CanDirectFetch(m_chainparams.GetConsensus())) { | |||||
m_connman.StartExtraBlockRelayPeers(); | |||||
m_initial_sync_finished = true; | |||||
} | |||||
} | |||||
void PeerManagerImpl::MaybeSendPing(CNode &node_to, Peer &peer) { | void PeerManagerImpl::MaybeSendPing(CNode &node_to, Peer &peer) { | ||||
// Use mockable time for ping timeouts. | // Use mockable time for ping timeouts. | ||||
// This means that setmocktime may cause pings to time out. | // This means that setmocktime may cause pings to time out. | ||||
auto now = GetTime<std::chrono::microseconds>(); | auto now = GetTime<std::chrono::microseconds>(); | ||||
if (m_connman.RunInactivityChecks(node_to) && peer.m_ping_nonce_sent && | if (m_connman.RunInactivityChecks(node_to) && peer.m_ping_nonce_sent && | ||||
now > | now > | ||||
peer.m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL}) { | peer.m_ping_start.load() + std::chrono::seconds{TIMEOUT_INTERVAL}) { | ||||
▲ Show 20 Lines • Show All 864 Lines • Show Last 20 Lines |