Skip to content

Commit c51e3e6

Browse files
sat-engineerclaude
andcommitted
feat: add Tor SOCKS5 proxy support for outbound .onion connections
Implement SOCKS5 protocol in connection.rs to route outbound peer connections through a Tor proxy. This enables LDK nodes to connect to peers at .onion addresses. Changes: - Add tor_socks5_connect() with full SOCKS5 handshake (RFC 1928/1929) - Support Tor stream isolation via random password auth per connection - Add set_tor_proxy_address() on NodeBuilder (FFI-compatible via UDL) - Route OnionV3 addresses through SOCKS5, clearnet through direct TCP - Include base32 encoder for OnionV3 address derivation Based on the approach in upstream ldk-node PR lightningdevkit#778, but with a self-contained SOCKS5 implementation that doesn't depend on unreleased lightning_net_tokio::tor_connect_outbound(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 99d12f4 commit c51e3e6

File tree

3 files changed

+696
-25
lines changed

3 files changed

+696
-25
lines changed

bindings/ldk_node.udl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ interface Builder {
104104
[Throws=BuildError]
105105
void set_announcement_addresses(sequence<SocketAddress> announcement_addresses);
106106
[Throws=BuildError]
107+
void set_tor_proxy_address(string tor_proxy_address);
108+
[Throws=BuildError]
107109
void set_node_alias(string node_alias);
108110
[Throws=BuildError]
109111
void set_async_payments_role(AsyncPaymentsRole? role);

src/builder.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ impl std::error::Error for BuildError {}
255255
#[derive(Debug)]
256256
pub struct NodeBuilder {
257257
config: Config,
258+
tor_proxy_address: Option<core::net::SocketAddr>,
258259
entropy_source_config: Option<EntropySourceConfig>,
259260
chain_data_source_config: Option<ChainDataSourceConfig>,
260261
gossip_source_config: Option<GossipSourceConfig>,
@@ -289,6 +290,7 @@ impl NodeBuilder {
289290
let pathfinding_scores_sync_config = None;
290291
Self {
291292
config,
293+
tor_proxy_address: None,
292294
entropy_source_config,
293295
chain_data_source_config,
294296
gossip_source_config,
@@ -593,6 +595,16 @@ impl NodeBuilder {
593595
Ok(self)
594596
}
595597

598+
/// Set the address which [`Node`] will use as a Tor proxy to connect to peer OnionV3 addresses.
599+
///
600+
/// **Note**: If unset, connecting to peer OnionV3 addresses will fail.
601+
///
602+
/// [`tor_proxy_address`]: Config::tor_proxy_address
603+
pub fn set_tor_proxy_address(&mut self, tor_proxy_address: core::net::SocketAddr) -> &mut Self {
604+
self.tor_proxy_address = Some(tor_proxy_address);
605+
self
606+
}
607+
596608
/// Sets the node alias that will be used when broadcasting announcements to the gossip
597609
/// network.
598610
///
@@ -985,6 +997,7 @@ impl NodeBuilder {
985997
logger,
986998
Arc::new(vss_store),
987999
self.reset_state,
1000+
self.tor_proxy_address,
9881001
)
9891002
}
9901003

@@ -1037,6 +1050,7 @@ impl NodeBuilder {
10371050
logger,
10381051
kv_store,
10391052
self.reset_state,
1053+
self.tor_proxy_address,
10401054
)
10411055
}
10421056
}
@@ -1304,6 +1318,19 @@ impl ArcedNodeBuilder {
13041318
self.inner.write().unwrap().set_announcement_addresses(announcement_addresses).map(|_| ())
13051319
}
13061320

1321+
/// Set the address which [`Node`] will use as a Tor proxy to connect to peer OnionV3 addresses.
1322+
///
1323+
/// The address should be in `host:port` format (e.g., `"127.0.0.1:9050"`).
1324+
///
1325+
/// **Note**: If unset, connecting to peer OnionV3 addresses will fail.
1326+
pub fn set_tor_proxy_address(&self, tor_proxy_address: String) -> Result<(), BuildError> {
1327+
let addr: core::net::SocketAddr = tor_proxy_address
1328+
.parse()
1329+
.map_err(|_| BuildError::InvalidListeningAddresses)?;
1330+
self.inner.write().unwrap().set_tor_proxy_address(addr);
1331+
Ok(())
1332+
}
1333+
13071334
/// Sets the node alias that will be used when broadcasting announcements to the gossip
13081335
/// network.
13091336
///
@@ -1416,6 +1443,7 @@ fn build_with_store_internal(
14161443
pathfinding_scores_sync_config: Option<&PathfindingScoresSyncConfig>,
14171444
async_payments_role: Option<AsyncPaymentsRole>, seed_bytes: [u8; 64], runtime: Arc<Runtime>,
14181445
logger: Arc<Logger>, kv_store: Arc<DynStore>, reset_state: Option<ResetState>,
1446+
tor_proxy_address: Option<core::net::SocketAddr>,
14191447
) -> Result<Node, BuildError> {
14201448
optionally_install_rustls_cryptoprovider();
14211449

@@ -2023,8 +2051,12 @@ fn build_with_store_internal(
20232051
Arc::clone(&runtime),
20242052
);
20252053

2026-
let connection_manager =
2027-
Arc::new(ConnectionManager::new(Arc::clone(&peer_manager), Arc::clone(&logger)));
2054+
let connection_manager = Arc::new(ConnectionManager::new(
2055+
Arc::clone(&peer_manager),
2056+
tor_proxy_address,
2057+
ephemeral_bytes,
2058+
Arc::clone(&logger),
2059+
));
20282060

20292061
let output_sweeper = match io::utils::read_output_sweeper(
20302062
Arc::clone(&tx_broadcaster),

0 commit comments

Comments
 (0)