From 127a4d23a5baecac868bac0067b02c9e20cd02f5 Mon Sep 17 00:00:00 2001 From: pasta Date: Tue, 23 Jul 2024 21:07:27 -0500 Subject: [PATCH 1/2] feat: aim to have 2 onion connections when possible, guard them from eviction In the past I've noticed that even when using `-proxy` over tor, I wouldn't actually gain any onion connections over time. This is even worse when using -onion. Sure it may expose an onion service, but you wouldn't gain any onion connections! The goal here is to minimize easy-ish censorship and improve network-wide resistance to partitioning. It is not unimaginable that port 9999 could be blocked at large scale. This could potentially result in severe partitioning, and subsequent issues. In an attempt to avoid this, we should always try to have at least 2 outbound onion connections when at all possible. Hopefully this also makes onion addresses gossip better. I don't think there is any real downside to this patch, stuff like masternode / quorum connections will still always happen over ipv4, but with this, blocks and transactions would continue to propogate across the network even if (non-onion) ipv4 traffic was all dropped. This also adds a benefit of p2p encryption for these peers. As a result, there is improved plausible deniability that you produced a transaction, as it is possible you received it over onion and simply rebroadcast it over ipv4. Arguably, it's not **ideal** to send so much traffic over tor, but hopefully as latency is higher, we will generally receive messages over ipv4 first and therefor not request them over the onion connections. --- src/init.cpp | 1 + src/net.cpp | 10 ++++++++++ src/net.h | 8 ++++++++ src/net_processing.cpp | 5 ++++- 4 files changed, 23 insertions(+), 1 deletion(-) 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 From e775b74d5e8cdf368a5cffd80b58b31bb031ff1f Mon Sep 17 00:00:00 2001 From: pasta Date: Tue, 23 Jul 2024 21:31:46 -0500 Subject: [PATCH 2/2] docs: add release notes for 6147 --- doc/release-notes-6147.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 doc/release-notes-6147.md 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