diff --git a/doc/release-notes-6147.md b/doc/release-notes-6147.md new file mode 100644 index 0000000000..c7b4df7b92 --- /dev/null +++ b/doc/release-notes-6147.md @@ -0,0 +1,12 @@ +P2P and Network Changes +----------------------- + +### Improved Onion Connectivity + +To enhance censorship resistance and mitigate network partitioning risks, Dash Core now aims to maintain at least two +outbound onion connections and generally protects these connections from eviction. As a result of the low percentage +of gossiped addresses being onion nodes, it was often the case where, unless you specify `onlynet=onion`, a node +would rarely if ever establish any outbound onion connections. This change ensures that nodes which can access the +onion network maintain a few onion connections. As a result, network messages could continue to propagate across +the network even if non-onion IPv4 traffic is blocked, reducing the risk of partitioning. Additionally, this update improves +security by enabling p2p encryption for these peers. (#6147) \ No newline at end of file diff --git a/src/init.cpp b/src/init.cpp index 82be1dc73a..1189c0c6ca 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2341,6 +2341,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) connOptions.nMaxConnections = nMaxConnections; connOptions.m_max_outbound_full_relay = std::min(MAX_OUTBOUND_FULL_RELAY_CONNECTIONS, connOptions.nMaxConnections); connOptions.m_max_outbound_block_relay = std::min(MAX_BLOCK_RELAY_ONLY_CONNECTIONS, connOptions.nMaxConnections-connOptions.m_max_outbound_full_relay); + connOptions.m_max_outbound_onion = std::min(MAX_DESIRED_ONION_CONNECTIONS, connOptions.nMaxConnections / 2); connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS; connOptions.nMaxFeeler = MAX_FEELER_CONNECTIONS; connOptions.uiInterface = &uiInterface; diff --git a/src/net.cpp b/src/net.cpp index 83f89edace..33a6c2e000 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2573,12 +2573,14 @@ void CConnman::ThreadOpenConnections(const std::vector connect, CDe // This is only done for mainnet and testnet int nOutboundFullRelay = 0; int nOutboundBlockRelay = 0; + int nOutboundOnionRelay = 0; std::set > setConnected; if (!Params().AllowMultipleAddressesFromGroup()) { LOCK(m_nodes_mutex); for (const CNode* pnode : m_nodes) { if (pnode->IsFullOutboundConn() && !pnode->m_masternode_connection) nOutboundFullRelay++; if (pnode->IsBlockOnlyConn()) nOutboundBlockRelay++; + if (pnode->IsFullOutboundConn() && pnode->ConnectedThroughNetwork() == Network::NET_ONION) nOutboundOnionRelay++; // Netgroups for inbound and manual peers are not excluded because our goal here // is to not use multiple of our limited outbound slots on a single netgroup @@ -2613,6 +2615,7 @@ void CConnman::ThreadOpenConnections(const std::vector connect, CDe auto now = GetTime(); bool anchor = false; bool fFeeler = false; + bool onion_only = false; // Determine what type of connection to open. Opening // BLOCK_RELAY connections to addresses from anchors.dat gets the highest @@ -2662,6 +2665,8 @@ void CConnman::ThreadOpenConnections(const std::vector connect, CDe next_feeler = PoissonNextSend(now, FEELER_INTERVAL); conn_type = ConnectionType::FEELER; fFeeler = true; + } else if (nOutboundOnionRelay < m_max_outbound_onion && IsReachable(Network::NET_ONION)) { + onion_only = true; } else { // skip to next iteration of while loop continue; @@ -2744,6 +2749,7 @@ void CConnman::ThreadOpenConnections(const std::vector connect, CDe if (!IsReachable(addr)) continue; + if (onion_only && !addr.IsTor()) continue; // only consider very recently tried nodes after 30 failed attempts if (nANow - addr_last_try < 600 && nTries < 30) @@ -3951,6 +3957,10 @@ size_t CConnman::GetMaxOutboundNodeCount() { return m_max_outbound; } +size_t CConnman::GetMaxOutboundOnionNodeCount() +{ + return m_max_outbound_onion; +} void CConnman::GetNodeStats(std::vector& vstats) const { diff --git a/src/net.h b/src/net.h index a0bb7bbba8..e916d58bdc 100644 --- a/src/net.h +++ b/src/net.h @@ -85,6 +85,8 @@ static const int MAX_ADDNODE_CONNECTIONS = 8; static const auto INBOUND_EVICTION_PROTECTION_TIME{1s}; /** Maximum number of block-relay-only outgoing connections */ static const int MAX_BLOCK_RELAY_ONLY_CONNECTIONS = 2; +/** Maximum number of onion connections we will try harder to connect to / protect from eviction */ +static const int MAX_DESIRED_ONION_CONNECTIONS = 2; /** Maximum number of feeler connections */ static const int MAX_FEELER_CONNECTIONS = 1; /** -listen default */ @@ -873,6 +875,7 @@ public: int nMaxConnections = 0; int m_max_outbound_full_relay = 0; int m_max_outbound_block_relay = 0; + int m_max_outbound_onion = 0; int nMaxAddnode = 0; int nMaxFeeler = 0; CClientUIInterface* uiInterface = nullptr; @@ -905,6 +908,7 @@ public: nMaxConnections = connOptions.nMaxConnections; m_max_outbound_full_relay = std::min(connOptions.m_max_outbound_full_relay, connOptions.nMaxConnections); m_max_outbound_block_relay = connOptions.m_max_outbound_block_relay; + m_max_outbound_onion = connOptions.m_max_outbound_onion; m_use_addrman_outgoing = connOptions.m_use_addrman_outgoing; nMaxAddnode = connOptions.nMaxAddnode; nMaxFeeler = connOptions.nMaxFeeler; @@ -1179,6 +1183,7 @@ public: size_t GetNodeCount(ConnectionDirection) const; size_t GetMaxOutboundNodeCount(); + size_t GetMaxOutboundOnionNodeCount(); void GetNodeStats(std::vector& vstats) const; bool DisconnectNode(const std::string& node); bool DisconnectNode(const CSubNet& subnet); @@ -1515,6 +1520,9 @@ private: // We do not relay tx or addr messages with these peers int m_max_outbound_block_relay; + // How many onion outbound peers we want; don't care if full or block only; does not increase m_max_outbound + int m_max_outbound_onion; + int nMaxAddnode; int nMaxFeeler; int m_max_outbound; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 384e1febeb..b02237e844 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -5179,9 +5179,12 @@ void PeerManagerImpl::EvictExtraOutboundPeers(std::chrono::seconds now) NodeId worst_peer = -1; int64_t oldest_block_announcement = std::numeric_limits::max(); + // We want to prevent recently connected to Onion peers from being disconnected here, protect them as long as + // there are more non_onion nodes than onion nodes so far + size_t onion_count = 0; m_connman.ForEachNode([&](CNode* pnode) { LockAssertion lock(::cs_main); - + if (pnode->addr.IsTor() && ++onion_count <= m_connman.GetMaxOutboundOnionNodeCount()) return; // Don't disconnect masternodes just because they were slow in block announcement if (pnode->m_masternode_connection) return; // Only consider outbound-full-relay peers that are not already