aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/net.cpp44
-rw-r--r--src/net.h15
-rw-r--r--src/net_processing.cpp52
-rw-r--r--src/net_processing.h4
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;
diff --git a/src/net.h b/src/net.h
index e959718fab..41f7fa93ff 100644
--- a/src/net.h
+++ b/src/net.h
@@ -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;
/**