diff options
| author | tv <tv@krebsco.de> | 2026-10-30 15:55:21 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-30 15:55:21 +0100 |
| commit | 055efc604d8cf56b2cdd05bbd863acb9c2ea12d6 (patch) | |
| tree | ef3f557b93f7daf3bef0c2cc6d0e38048bec4377 | |
| parent | 54a8d7b3409f6a5857b6fc9a964a36b66c28ae25 (diff) | |
make mycelium optional
| -rw-r--r-- | .cargo/config.toml | 4 | ||||
| -rw-r--r-- | Cargo.toml | 9 | ||||
| -rw-r--r-- | src/config/mod.rs | 34 | ||||
| -rw-r--r-- | src/transport/mod.rs | 9 | ||||
| -rw-r--r-- | src/transport/paths.rs | 2 | ||||
| -rw-r--r-- | src/transport/sim.rs | 26 |
6 files changed, 71 insertions, 13 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml index 8660776..6288d9d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,6 @@ # Shortcuts for the development loop (docs/development.md). [alias] # Everything but the simulations: a few seconds. -quick = "test --features libp2p,tor -- --skip simtest" +quick = "test --features mycelium,libp2p,tor -- --skip simtest" # The simulations: whole swarms in one process, in real time. -sim = "test --features libp2p,tor simtest" +sim = "test --features mycelium,libp2p,tor simtest" @@ -3,8 +3,13 @@ name = "continuum" version = "1.0.0" edition = "2024" +# The transports are features, none of them default: a build has those it names, at +# least one (`--features mycelium`, `--features libp2p,tor`, ...). [features] tokio-console = ["dep:console-subscriber"] +# The Mycelium transport (`--transports mycelium`, the default where built): through a +# Mycelium daemon's API, one it supervises or one running elsewhere. +mycelium = ["dep:reqwest", "dep:libc"] # The libp2p transport (`--transports libp2p`). libp2p = ["dep:libp2p", "dep:libp2p-stream", "dep:futures"] # The Tor transport (`--transports tor`): onion services, with arti. @@ -44,9 +49,9 @@ libp2p = { version = "0.57", optional = true, features = ["tokio", "tcp", "quic" libp2p-stream = { version = "0.5.0-alpha", optional = true } ed25519-dalek = "3" getrandom = "0.4" -libc = "0.2" +libc = { version = "0.2", optional = true } password-hash = "0.6" -reqwest = { version = "0.13", features = ["json", "query"] } +reqwest = { version = "0.13", optional = true, features = ["json", "query"] } rmp-serde = "1.3" serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11" diff --git a/src/config/mod.rs b/src/config/mod.rs index 99690ae..0c57713 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -260,8 +260,12 @@ pub(crate) const OPTIONS: &[OptionSpec] = &[ "transports", Kind::List, "NAME,[NAME...]", - Some("mycelium"), - "The transports this node uses, in order of preference: mycelium, libp2p and tor (in builds with the libp2p and tor features). A peer is reached on the first one both have, until the paths are measured (then on the fastest); nodes on several carry the traffic between nodes that share none.", + if cfg!(feature = "mycelium") { + Some("mycelium") + } else { + None + }, + "The transports this node uses, in order of preference: mycelium, libp2p and tor (each in builds with its cargo feature; mycelium is the default where built). A peer is reached on the first one both have, until the paths are measured (then on the fastest); nodes on several carry the traffic between nodes that share none.", ), option( "libp2p-listen", @@ -501,12 +505,18 @@ pub(crate) struct Settings { pub(crate) tor_port: u16, #[serde(default)] pub(crate) relay_only: bool, + #[cfg_attr(not(feature = "mycelium"), allow(dead_code))] pub(crate) mycelium_rest_url: Option<String>, + #[cfg_attr(not(feature = "mycelium"), allow(dead_code))] pub(crate) mycelium_key_file: Option<PathBuf>, + #[cfg_attr(not(feature = "mycelium"), allow(dead_code))] pub(crate) mycelium_network_key_file: Option<PathBuf>, + #[cfg_attr(not(feature = "mycelium"), allow(dead_code))] pub(crate) mycelium_network_name: Option<String>, + #[cfg_attr(not(feature = "mycelium"), allow(dead_code))] pub(crate) mycelium_tcp_listen_port: Option<u16>, #[serde(default = "default_mycelium_peers")] + #[cfg_attr(not(feature = "mycelium"), allow(dead_code))] pub(crate) mycelium_peers: Vec<String>, pub(crate) debug_epoch: Option<u64>, } @@ -532,12 +542,17 @@ fn default_electorate_window() -> Duration { fn default_blob_chunk() -> u64 { 64 * 1024 } +/// Mycelium where built; else none, so the transports must be named. fn default_transports() -> Vec<String> { - vec!["mycelium".to_owned()] + if cfg!(feature = "mycelium") { + vec!["mycelium".to_owned()] + } else { + Vec::new() + } } /// The transports `transports` may name, and whether this build has each. pub(crate) const TRANSPORTS: &[(&str, bool)] = &[ - ("mycelium", true), + ("mycelium", cfg!(feature = "mycelium")), ("libp2p", cfg!(feature = "libp2p")), ("tor", cfg!(feature = "tor")), ]; @@ -553,6 +568,7 @@ fn default_mycelium_peers() -> Vec<String> { impl Settings { /// `mycelium-rest-url`, parsed (checked when loading). + #[cfg_attr(not(feature = "mycelium"), allow(dead_code))] pub(crate) fn mycelium_rest_url(&self) -> Option<url::Url> { self.mycelium_rest_url .as_deref() @@ -628,7 +644,15 @@ impl Settings { } } if self.transports.is_empty() { - bail!("transports: a node needs at least one"); + bail!( + "transports: a node needs at least one (this build has: {})", + TRANSPORTS + .iter() + .filter(|(_, built)| *built) + .map(|(name, _)| *name) + .collect::<Vec<_>>() + .join(", ") + ); } if self.transports.iter().any(|name| name == "tor") && self.tor_state_dir.is_none() { bail!( diff --git a/src/transport/mod.rs b/src/transport/mod.rs index 9d43e5a..d8c6e2b 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -18,6 +18,7 @@ pub(crate) mod firewall; pub(crate) mod identity; #[cfg(feature = "libp2p")] pub(crate) mod libp2p; +#[cfg(feature = "mycelium")] pub(crate) mod mycelium; pub(crate) mod paths; #[cfg(test)] @@ -28,6 +29,11 @@ pub(crate) mod tor; pub(crate) mod traffic; pub(crate) mod wire; +#[cfg(not(any(feature = "mycelium", feature = "libp2p", feature = "tor")))] +compile_error!( + "continuum needs a transport: build with at least one of the mycelium, libp2p and tor features, e.g. `cargo build --features mycelium`" +); + /// Starts the transport `name` (one of `config::TRANSPORTS`) as `settings` configure /// it: its scheme and the transport, or None if `stop` came first (some take a while to /// start: a supervised Mycelium daemon, arti). `key`: the node key, which some prove @@ -39,8 +45,9 @@ pub(crate) async fn start( stop: tokio::sync::watch::Receiver<bool>, ) -> Result<Option<(&'static str, std::sync::Arc<dyn Transport>)>> { use std::sync::Arc; - let _ = key; + let _ = (key, &stop); let started: Option<(&'static str, Arc<dyn Transport>)> = match name { + #[cfg(feature = "mycelium")] "mycelium" => mycelium::start(settings, stop) .await? .map(|transport| (mycelium::SCHEME, Arc::new(transport) as Arc<dyn Transport>)), diff --git a/src/transport/paths.rs b/src/transport/paths.rs index e8858da..4d0e775 100644 --- a/src/transport/paths.rs +++ b/src/transport/paths.rs @@ -1228,7 +1228,7 @@ mod tests { Ok(seed.to_owned()) } fn encode(&self, peer: &str, message: &ParsedMessage) -> Result<EncodedMessage> { - crate::transport::mycelium::encode_message(peer, message) + crate::transport::sim::encode_message(peer, message) } async fn send_encoded(&self, _message: &EncodedMessage) -> Result<()> { Err(anyhow!("broken")) diff --git a/src/transport/sim.rs b/src/transport/sim.rs index 500050b..2410bba 100644 --- a/src/transport/sim.rs +++ b/src/transport/sim.rs @@ -2,7 +2,8 @@ //! [`SimTransport`] on a shared [`SimNet`]. //! //! Messages go through the Mycelium wire encoding (`mycelium::encode_message`), so the -//! codec is exercised as in production. The net delays, loses and duplicates them per +//! codec is exercised as in production; without the mycelium feature, through the wire +//! encoding alone, as libp2p and Tor send it. The net delays, loses and duplicates them per //! link, from a seeded generator; nodes can be taken down (everything to and from them //! is lost) and brought up again; and a test can inject a message as if a node had sent //! it, e.g. one that arrives late. @@ -24,7 +25,8 @@ use bytes::Bytes; use tokio::sync::{mpsc, oneshot}; use tracing::{debug, warn}; -use super::mycelium::{decode_message, encode_message}; +#[cfg(feature = "mycelium")] +pub(crate) use super::mycelium::{decode_message, encode_message}; use super::{EncodedMessage, MessageHandler, MessageKind, ParsedMessage, Transport}; use crate::locks::MutexExt; use crate::testrand::Rng; @@ -249,6 +251,26 @@ pub(crate) struct SimTransport { inbox: tokio::sync::Mutex<Option<mpsc::UnboundedReceiver<Delivery>>>, } +/// Without Mycelium: a message in the wire encoding alone, as libp2p and Tor send it. +#[cfg(not(feature = "mycelium"))] +pub(crate) fn encode_message(peer: &str, message: &ParsedMessage) -> Result<EncodedMessage> { + let body = super::wire::encode(message)?; + Ok(EncodedMessage { + peer: peer.to_owned(), + address: peer.to_owned(), + payload_len: body.len(), + body: body.into(), + kind: message.kind(), + message: None, + }) +} + +/// The message in a body made by [`encode_message`]. +#[cfg(not(feature = "mycelium"))] +pub(crate) fn decode_message(body: &[u8]) -> Result<ParsedMessage> { + Ok(super::wire::decode(body)?) +} + #[async_trait] impl Transport for SimTransport { fn local_id(&self) -> &str { |
