From f091fb2a9188f686e5b1f9012c510a1b3c8ad840 Mon Sep 17 00:00:00 2001 From: tv Date: Fri, 30 Oct 2026 16:32:23 +0100 Subject: move transport config checks to respective modules --- src/transport/libp2p.rs | 59 ++++++++++++++++++++++++++++++++----------- src/transport/mod.rs | 21 +++++++++++++++ src/transport/mycelium/mod.rs | 20 +++++++++++++++ src/transport/tor.rs | 11 ++++++++ 4 files changed, 96 insertions(+), 15 deletions(-) (limited to 'src/transport') diff --git a/src/transport/libp2p.rs b/src/transport/libp2p.rs index 2d67d2c..d023079 100644 --- a/src/transport/libp2p.rs +++ b/src/transport/libp2p.rs @@ -204,6 +204,48 @@ pub(crate) struct Libp2pConfig { pub(crate) memory: bool, } +/// The multiaddrs of option `option`. +fn parse_addresses(option: &str, addresses: &[String]) -> Result> { + addresses + .iter() + .map(|address| { + address + .parse::() + .with_context(|| format!("{option} {address:?}")) + }) + .collect() +} + +/// The ranges of `libp2p-ignore`. +fn parse_ranges(ranges: &[String]) -> Result> { + ranges + .iter() + .map(|range| range.parse().context("libp2p-ignore")) + .collect() +} + +/// This transport's options, checked when the configuration is loaded +/// (`config::Settings::check`), so `--print-config` tells a wrong one too: the +/// multiaddrs and ranges parse, and a relay that is no node (`--relay-only`) can be +/// dialled and keeps its peer ID. +pub(crate) fn check(settings: &crate::config::Settings) -> Result<()> { + parse_addresses("libp2p-listen", &settings.libp2p_listen)?; + parse_addresses("libp2p-announce", &settings.libp2p_announce)?; + parse_addresses("libp2p-relays", &settings.libp2p_relays)?; + parse_ranges(&settings.libp2p_ignore)?; + if settings.relay_only { + if settings.libp2p_listen.is_empty() { + bail!("relay-only needs libp2p-listen: where nodes reach the relay"); + } + if settings.libp2p_key_file.is_none() { + bail!( + "relay-only needs libp2p-key-file: without it, the relay's peer ID changes with every start and nodes lose it" + ); + } + } + Ok(()) +} + impl Libp2pConfig { /// The configuration `settings` give; `relay_only`: a relay that is no node /// (`--relay-only`). @@ -211,16 +253,7 @@ impl Libp2pConfig { settings: &crate::config::Settings, relay_only: bool, ) -> Result { - let parse = |option: &str, addresses: &[String]| { - addresses - .iter() - .map(|address| { - address - .parse::() - .with_context(|| format!("{option} {address:?}")) - }) - .collect::>>() - }; + let parse = parse_addresses; Ok(Self { key: Self::load_key(settings.libp2p_key_file.as_deref())?, listen: parse("libp2p-listen", &settings.libp2p_listen)?, @@ -230,11 +263,7 @@ impl Libp2pConfig { continuum: !relay_only, mdns: settings.libp2p_mdns, upnp: settings.libp2p_upnp, - ignore: settings - .libp2p_ignore - .iter() - .map(|range| range.parse().context("libp2p-ignore")) - .collect::>>()?, + ignore: parse_ranges(&settings.libp2p_ignore)?, public: settings.libp2p_public, memory: false, }) diff --git a/src/transport/mod.rs b/src/transport/mod.rs index d8c6e2b..fd3651b 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -34,6 +34,27 @@ compile_error!( "continuum needs a transport: build with at least one of the mycelium, libp2p and tor features, e.g. `cargo build --features mycelium`" ); +/// The options of the transports this build has, each checked by its transport when the +/// configuration is loaded (`config::Settings::check`, after the names in `transports`): +/// Mycelium's always (its conflicts are errors wherever they come from, as before); +/// libp2p's and Tor's if the node uses them. Options of transports not built aren't +/// checked: they're ignored, and said so (`config::Loaded::unused_transport_options`). +pub(crate) fn check(settings: &crate::config::Settings) -> Result<()> { + let uses = |name: &str| settings.transports.iter().any(|used| used == name); + let _ = uses; + #[cfg(feature = "mycelium")] + mycelium::check(settings)?; + #[cfg(feature = "libp2p")] + if uses("libp2p") { + libp2p::check(settings)?; + } + #[cfg(feature = "tor")] + if uses("tor") { + tor::TorTransport::check(settings)?; + } + Ok(()) +} + /// 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 diff --git a/src/transport/mycelium/mod.rs b/src/transport/mycelium/mod.rs index 7abcced..20abee8 100644 --- a/src/transport/mycelium/mod.rs +++ b/src/transport/mycelium/mod.rs @@ -91,6 +91,26 @@ pub(crate) struct MyceliumTransport { local_id: String, } +/// This transport's options, checked when the configuration is loaded +/// (`config::Settings::check`): a daemon elsewhere (`mycelium-rest-url`) by a URL, and +/// none of the options only a supervised one takes with it; a private network with +/// both its key and its name. +pub(crate) fn check(settings: &crate::config::Settings) -> Result<()> { + if let Some(url) = &settings.mycelium_rest_url { + Url::parse(url).with_context(|| format!("mycelium-rest-url {url:?}"))?; + if let Some((name, _)) = settings.supervised_only().iter().find(|(_, set)| *set) { + anyhow::bail!( + "{name} is for a supervised Mycelium daemon and can't be combined with mycelium-rest-url" + ); + } + } + // A private network has both; one alone would be ignored or invented. + if settings.mycelium_network_key_file.is_some() != settings.mycelium_network_name.is_some() { + anyhow::bail!("mycelium-network-key-file and mycelium-network-name go together"); + } + Ok(()) +} + /// The transport as `settings` configure it (`transport::start`): its daemon supervised /// (started, which can take a while) or one running elsewhere. None if `stop` came /// first. diff --git a/src/transport/tor.rs b/src/transport/tor.rs index 8eb1d02..f2e0199 100644 --- a/src/transport/tor.rs +++ b/src/transport/tor.rs @@ -198,6 +198,17 @@ impl TorTransport { /// while: it fetches what it needs to know of the Tor network). Until it is done, /// dials wait for it (arti bootstraps on demand), and the onion service is published /// once it is: the node starts without waiting for Tor. + /// This transport's options, checked when the configuration is loaded + /// (`config::Settings::check`), if the node uses it: where arti keeps its state. + pub(crate) fn check(settings: &crate::config::Settings) -> Result<()> { + if settings.tor_state_dir.is_none() { + bail!( + "tor needs tor-state-dir: where arti keeps its state, the onion service's key among it" + ); + } + Ok(()) + } + /// The transport as `settings` configure it (`transport::start`), proving `key`. /// None if `stop` came first (bootstrapping arti can take a while). pub(crate) async fn from_settings( -- cgit v1.3.1