diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/net.cpp | 44 | ||||
-rw-r--r-- | src/net.h | 15 | ||||
-rw-r--r-- | src/net_processing.cpp | 52 | ||||
-rw-r--r-- | src/net_processing.h | 4 |
4 files changed, 111 insertions, 4 deletions
diff --git a/src/net.cpp b/src/net.cpp index fe1baf97a3..7cb91f1388 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1841,6 +1841,20 @@ int CConnman::GetExtraFullOutboundCount() return std::max(full_outbound_peers - m_max_outbound_full_relay, 0); } +int CConnman::GetExtraBlockRelayCount() +{ + int block_relay_peers = 0; + { + LOCK(cs_vNodes); + for (const CNode* pnode : vNodes) { + if (pnode->fSuccessfullyConnected && !pnode->fDisconnect && pnode->IsBlockOnlyConn()) { + ++block_relay_peers; + } + } + } + return std::max(block_relay_peers - m_max_outbound_block_relay, 0); +} + void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) { // Connect to specific addresses @@ -1869,6 +1883,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) // Minimum time before next feeler connection (in microseconds). int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL); + int64_t nNextExtraBlockRelay = PoissonNextSend(nStart*1000*1000, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL); while (!interruptNet) { ProcessAddrFetch(); @@ -1941,8 +1956,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) // until we hit our block-relay-only peer limit. // GetTryNewOutboundPeer() gets set when a stale tip is detected, so we // try opening an additional OUTBOUND_FULL_RELAY connection. If none of - // these conditions are met, check the nNextFeeler timer to decide if - // we should open a FEELER. + // these conditions are met, check to see if it's time to try an extra + // block-relay-only peer (to confirm our tip is current, see below) or the nNextFeeler + // timer to decide if we should open a FEELER. if (!m_anchors.empty() && (nOutboundBlockRelay < m_max_outbound_block_relay)) { conn_type = ConnectionType::BLOCK_RELAY; @@ -1953,6 +1969,30 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) conn_type = ConnectionType::BLOCK_RELAY; } else if (GetTryNewOutboundPeer()) { // OUTBOUND_FULL_RELAY + } else if (nTime > nNextExtraBlockRelay && m_start_extra_block_relay_peers) { + // Periodically connect to a peer (using regular outbound selection + // methodology from addrman) and stay connected long enough to sync + // headers, but not much else. + // + // Then disconnect the peer, if we haven't learned anything new. + // + // The idea is to make eclipse attacks very difficult to pull off, + // because every few minutes we're finding a new peer to learn headers + // from. + // + // This is similar to the logic for trying extra outbound (full-relay) + // peers, except: + // - we do this all the time on a poisson timer, rather than just when + // our tip is stale + // - we potentially disconnect our next-youngest block-relay-only peer, if our + // newest block-relay-only peer delivers a block more recently. + // See the eviction logic in net_processing.cpp. + // + // Because we can promote these connections to block-relay-only + // connections, they do not get their own ConnectionType enum + // (similar to how we deal with extra outbound peers). + nNextExtraBlockRelay = PoissonNextSend(nTime, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL); + conn_type = ConnectionType::BLOCK_RELAY; } else if (nTime > nNextFeeler) { nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL); conn_type = ConnectionType::FEELER; @@ -48,6 +48,8 @@ static const bool DEFAULT_WHITELISTFORCERELAY = false; static const int TIMEOUT_INTERVAL = 20 * 60; /** Run the feeler connection loop once every 2 minutes or 120 seconds. **/ static const int FEELER_INTERVAL = 120; +/** Run the extra block-relay-only connection loop once every 5 minutes. **/ +static const int EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL = 300; /** The maximum number of addresses from our addrman to return in response to a getaddr message. */ static constexpr size_t MAX_ADDR_TO_SEND = 1000; /** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */ @@ -330,6 +332,11 @@ public: void SetTryNewOutboundPeer(bool flag); bool GetTryNewOutboundPeer(); + void StartExtraBlockRelayPeers() { + LogPrint(BCLog::NET, "net: enabling extra block-relay-only peers\n"); + m_start_extra_block_relay_peers = true; + } + // Return the number of outbound peers we have in excess of our target (eg, // if we previously called SetTryNewOutboundPeer(true), and have since set // to false, we may have extra peers that we wish to disconnect). This may @@ -337,6 +344,8 @@ public: // in cases where some outbound connections are not yet fully connected, or // not yet fully disconnected. int GetExtraFullOutboundCount(); + // Count the number of block-relay-only peers we have over our limit. + int GetExtraBlockRelayCount(); bool AddNode(const std::string& node); bool RemoveAddedNode(const std::string& node); @@ -594,6 +603,12 @@ private: * This takes the place of a feeler connection */ std::atomic_bool m_try_another_outbound_peer; + /** flag for initiating extra block-relay-only peer connections. + * this should only be enabled after initial chain sync has occurred, + * as these connections are intended to be short-lived and low-bandwidth. + */ + std::atomic_bool m_start_extra_block_relay_peers{false}; + std::atomic<int64_t> m_next_send_inv_to_incoming{0}; /** diff --git a/src/net_processing.cpp b/src/net_processing.cpp index ffbd2c17f9..7dee2db3c8 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -3909,9 +3909,52 @@ void PeerManager::ConsiderEviction(CNode& pto, int64_t time_in_seconds) void PeerManager::EvictExtraOutboundPeers(int64_t time_in_seconds) { + // If we have any extra block-relay-only peers, disconnect the youngest unless + // it's given us a block -- in which case, compare with the 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 peers - int extra_peers = m_connman.GetExtraFullOutboundCount(); - if (extra_peers > 0) { + if (m_connman.GetExtraFullOutboundCount() > 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 @@ -3983,6 +4026,11 @@ void PeerManager::CheckForStaleTipAndEvictPeers() } 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; + } } namespace { diff --git a/src/net_processing.h b/src/net_processing.h index d5b54dae56..12a4e9c38f 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -206,6 +206,10 @@ private: //* Whether this node is running in blocks only mode */ 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 */ mutable Mutex m_peer_mutex; /** |