Skip to content

Comments

Add Tor support to Cuprate (Arti, Tor Daemon, Dandelion router)#509

Merged
Boog900 merged 17 commits intomainfrom
tor
Aug 4, 2025
Merged

Add Tor support to Cuprate (Arti, Tor Daemon, Dandelion router)#509
Boog900 merged 17 commits intomainfrom
tor

Conversation

@SyntheticBird45
Copy link
Member

@SyntheticBird45 SyntheticBird45 commented Jun 10, 2025

What

Part of ongoing Tor support development for Cuprate

Follows: #446 and #481

Changelog

In workspace:

  • New dependencies: arti_client, Cuprate/tokio-socks.git, tor-cell, tor-config-path, tor-hsservice, tor-persist, tor-proto, tor-rtcompat (yes nothing was exported).
  • In deny.toml, whitelisted Unlicense license for arti_client.

In cuprate-p2p-transport:

  • Implemented Transport<ClearNet> and Transport<Tor> for Arti. New ArtiClientConfig, ArtiServerConfig configuration. New OnionListener for accepting inbound connections from arti's generated onion service.
  • Implemented Transport<Tor> for Daemon. New DaemonClientConfig, DaemonServerConfig configuration. New DaemonInboundStream listening for incoming TCP connections from the tor daemon.
  • DisabledListener as a polyfill for transports with inbound disabled, such as Transport<ClearNet> for Arti and in the future Transport<ClearNet> for Socks5.

In cuprate-p2p-core:

  • Removed Default and Debug bound from Transport::ClientConfig and Transport::ServerConfig.
  • Removed Clone bound from Transport::ServerConfig.

In cuprate-p2p:

  • Changed some function visibility to pub(super) instead of pub.

In cuprate-wire:

  • Added borsh dependency and BorshSerialize and BorshDeserialize derived implementation to OnionAddr for BorshNetworkZone requirement in address book.

In cuprated:

  • New tor module containing the initialization of Arti, config helpers and context structure TorContext to pass down to p2p initialization function and other helpers.
  • New config/tor module containing the [tor] configuration table. It define tor daemon related variables, as well as arti settings.
  • Added enable_inbound field to ClearNetConfig to disable incoming listener by default.
  • Added proxy field to ClearNetConfig for enabling clearnet over arti and in the future proxy urls.
  • Added TorNetConfig for setting Tor network zone parameters such as listening ports, enabling arti inbound server, or setting an anonymous inbound onion address from an external daemon.
  • Modified initialize_zones_p2p to now start Tor network zone and use the correct transport depending on user configuration.
  • In txpool/*, generalized DiffuseService, OutboundPeerStream and ConcreteDandelionRouter for Z: NetworkZone. Created a new MainDandelionRouter service that will route local txs to a Tor router instead of clearnet if available. Adapted initialization to the changes.

@github-actions github-actions bot added A-p2p Area: Related to P2P. A-dependency Area: Related to dependencies, or changes to a Cargo.{toml,lock} file. A-workspace Area: Changes to a root workspace file or general repo file. A-storage Area: Related to storage. A-net Area: Related to networking. A-binaries Area: Related to binaries. labels Jun 10, 2025
@SyntheticBird45 SyntheticBird45 force-pushed the tor branch 3 times, most recently from 8faf6ef to 8c14140 Compare June 10, 2025 22:59
@github-actions github-actions bot removed the A-storage Area: Related to storage. label Jun 10, 2025
@SyntheticBird45 SyntheticBird45 force-pushed the tor branch 4 times, most recently from 83ee8dc to cf16130 Compare June 11, 2025 13:26
@binarybaron
Copy link

very cool!

In `workspace`:
- New dependencies: `arti_client`, `Cuprate/tokio-socks.git`, `tor-cell`, `tor-config-path`, `tor-hsservice`, `tor-persist`, `tor-proto`, `tor-rtcompat` (yes nothing was exported).
- In `deny.toml`, whitelisted `Unlicense` license for `arti_client`.

In `cuprate-p2p-transport`:
- Implemented `Transport<ClearNet>` and `Transport<Tor>` for `Arti`. New `ArtiClientConfig`, `ArtiServerConfig` configuration. New `OnionListener` for accepting inbound connections from arti's generated onion service.
- Implemented `Transport<Tor>` for `Daemon`. New `DaemonClientConfig`, `DaemonServerConfig` configuration. New `DaemonInboundStream` listening for incoming TCP  connections from the tor daemon.
- `DisabledListener` as a polyfill for transports with inbound disabled, such as `Transport<ClearNet> for Arti` and in the future `Transport<ClearNet> for Socks5`.

In `cuprate-p2p-core`:
- Removed `Default` and `Debug` bound from `Transport::ClientConfig` and `Transport::ServerConfig`.
- Removed `Clone` bound from `Transport::ServerConfig`.

In `cuprate-p2p`:
- Changed some function visibility to `pub(super)` instead of `pub`.

In `cuprate-wire`:
- Added `borsh` dependency and `BorshSerialize` and `BorshDeserialize` derived implementation to `OnionAddr` for `BorshNetworkZone` requirement in address book.

In `cuprated`:
- New `tor` module containing the initialization of Arti, config helpers and context structure `TorContext` to pass down to p2p initialization function and other helpers.
- New `config/tor` module containing the `[tor]` configuration table. It define tor daemon related variables, as well as arti settings.
- Added `enable_inbound` field to `ClearNetConfig` to disable incoming listener by default.
- Added `proxy` field to `ClearNetConfig` for enabling clearnet over arti and in the future proxy urls.
- Added `TorNetConfig` for setting `Tor` network zone parameters such as listening ports, enabling arti inbound server, or setting an anonymous inbound onion address from an external daemon.
- Modified `initialize_zones_p2p` to now start Tor network zone and use the correct transport depending on user configuration.
- In `txpool/*`, generalized `DiffuseService`, `OutboundPeerStream` and `ConcreteDandelionRouter` for `Z: NetworkZone`. Created a new `MainDandelionRouter` service that will route local txs to a Tor router instead of clearnet if available. Adapted initialization to the changes.
@SyntheticBird45 SyntheticBird45 marked this pull request as ready for review June 13, 2025 21:45
@hinto-janai hinto-janai mentioned this pull request Jun 16, 2025
Copy link
Member

@Boog900 Boog900 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first pass, good work!

/// clearnet as a proxy.
pub async fn initialize_tor_if_enabled(config: &Config) -> TorContext {
let mode = config.tor.mode;
let anonymize_clearnet = config.p2p.clear_net.proxy.to_lowercase() == "Tor";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to_lowercase Tor :)

Do you think it might be good idea to make proxy an enum?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to_lowercase Tor :)

lol

Do you think it might be good idea to make proxy an enum?

It might be but I prefer to think about it in the SOCKS5 PR that comes just after (with docs PR) because fwiw I haven't decided if we should just support SOCKS5 and not SOCKS5H or 4. And therefore if we should rely on some URI library with serde support. we could handroll too. I prefer to think of String as a placeholder atm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the enum I was thinking would be something like:

enum {
    Tor,
    Socks(String)
}

with the socks being a catch all, this is a nit but does improve the code over string matching 🤷

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. thx for clarifying

Comment on lines 137 to 142
let Some(bootstrapped_client) = ctx.bootstrapped_client else {
panic!("Arti client should be initialized");
};
let Some(client_config) = ctx.arti_client_config else {
panic!("Arti client should be initialized");
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bit of nit but these can be combined:

Suggested change
let Some(bootstrapped_client) = ctx.bootstrapped_client else {
panic!("Arti client should be initialized");
};
let Some(client_config) = ctx.arti_client_config else {
panic!("Arti client should be initialized");
};
let (Some(bootstrapped_client), Some(client_config)) = (ctx.bootstrapped_client, ctx.arti_client_config) else {
panic!("Arti client should be initialized");
};

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like having for conditions for one panic on the same line. If it panic I'll wonder what was uninitialized.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well then the comments should be different and mention the field IMO

client_config: DaemonClientConfig {
tor_daemon: config.tor.daemon.address,
},
server_config: (!config.tor.daemon.anonymous_inbound.is_empty()).then_some(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this instead check the bool field?

Copy link
Member Author

@SyntheticBird45 SyntheticBird45 Jun 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should check bool and warn if anonymous_inbound is empty

allow = [
# Nothing required - free to use without permission.
"CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/
"Unlicense",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to instead list the crates to allow?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are quite a lot of them. Every subcrates of arti is Unlicense

@github-actions github-actions bot added A-cryptonight Area: Related to Cryptonight. A-rpc Area: Related to RPC. labels Jul 1, 2025
@hinto-janai hinto-janai added this to the cuprated v0.0.6 milestone Jul 15, 2025
@SyntheticBird45 SyntheticBird45 requested a review from Boog900 July 24, 2025 09:10
@Boog900
Copy link
Member

Boog900 commented Aug 1, 2025

Still have some changes I want to make, will put a comment when I am done.

@github-actions github-actions bot added A-consensus Area: Related to consensus. and removed A-cryptonight Area: Related to Cryptonight. labels Aug 1, 2025
@github-actions github-actions bot added the A-ci Area: Related to CI. label Aug 1, 2025
@Boog900
Copy link
Member

Boog900 commented Aug 2, 2025

@SyntheticBird45 If you can review my changes I'll merge

@SyntheticBird45
Copy link
Member Author

SyntheticBird45 commented Aug 4, 2025

@SyntheticBird45 If you can review my changes I'll merge

Approved. (I can't approve my own PR) No comments, changes are clear and reasonable. Thanks for taking finishing it.

If you could rebase and update this commit message e66d550 (and also add yourself as co author) that would be perfect.

@Boog900 Boog900 merged commit 9c2c942 into main Aug 4, 2025
20 checks passed
@Boog900 Boog900 deleted the tor branch August 23, 2025 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-binaries Area: Related to binaries. A-ci Area: Related to CI. A-consensus Area: Related to consensus. A-dependency Area: Related to dependencies, or changes to a Cargo.{toml,lock} file. A-net Area: Related to networking. A-p2p Area: Related to P2P. A-rpc Area: Related to RPC. A-workspace Area: Changes to a root workspace file or general repo file.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants