//! What the options make of the libp2p transport (`Libp2pConfig`), and their checks. use super::*; /// What a libp2p transport is made with. pub(crate) struct Libp2pConfig { pub(crate) key: Keypair, /// Multiaddrs to listen on. pub(crate) listen: Vec, /// Multiaddrs to publish instead of the listen addresses, if any. pub(crate) announce: Vec, /// Relays to reserve a slot on (multiaddrs; without the relay's peer ID, a connection /// finds it out). pub(crate) relays: Vec, /// Relay for other nodes. pub(crate) relay: bool, /// Speak continuum (`PROTOCOL`): not on a node that only relays (`--relay-only`), /// which nobody should take for a node. pub(crate) continuum: bool, /// Find peers on the LAN (mDNS). pub(crate) mdns: bool, /// Ask the router to forward our listen ports (UPnP), and publish where. pub(crate) upnp: bool, /// Address ranges neither published nor dialled, nor connected over (`Ignoring`). pub(crate) ignore: Vec, /// Address ranges that matter most, best first (`Book::preference`). pub(crate) prefer: Vec, /// Hosts on the internet (names or addresses) whose routes tell whether there is /// one to the internet at all (`Routes::internet`). pub(crate) internet_hosts: Vec, /// Use public infrastructure: the IPFS DHT, public relays. pub(crate) public: bool, /// libp2p's in-memory transport instead of TCP and QUIC: for tests. pub(crate) memory: bool, /// The node's keepalive interval (`--keepalive`): connections idle for a few of /// them are closed (`idle_timeout`). pub(crate) keepalive: Duration, } /// The multiaddrs of option `option`. pub(super) fn parse_addresses(option: &str, addresses: &[String]) -> Result> { addresses .iter() .map(|address| { address .parse::() .with_context(|| format!("{option} {address:?}")) }) .collect() } /// The ranges of option `option`. pub(super) fn parse_ranges(option: &str, ranges: &[String]) -> Result> { ranges .iter() .map(|range| range.parse().with_context(|| option.to_owned())) .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)?; let ignore = parse_ranges("libp2p-ignore", &settings.libp2p_ignore)?; let prefer = parse_ranges("libp2p-prefer", &settings.libp2p_prefer)?; check_preferred( &ignore, &prefer, &settings.libp2p_ignore, &settings.libp2p_prefer, )?; if settings.libp2p_key_create && settings.libp2p_key_file.is_none() { bail!("libp2p-key-create creates the key at libp2p-key-file; set that too"); } 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(()) } /// A range both ignored and preferred: refused where all of the preferred one is /// ignored (it would name nothing usable), a warning where only part of it is (the /// ignored part stays left out). `ignore_given` and `prefer_given`: as written, for /// the messages. Ranges nest or are apart, so there's nothing in between. pub(super) fn check_preferred( ignore: &[IpRange], prefer: &[IpRange], ignore_given: &[String], prefer_given: &[String], ) -> Result<()> { for (preferred, preferred_given) in prefer.iter().zip(prefer_given) { for (ignored, ignored_given) in ignore.iter().zip(ignore_given) { if ignored.covers(preferred) { bail!( "libp2p-prefer {preferred_given:?} is all in libp2p-ignore {ignored_given:?}: nothing there is used" ); } if preferred.covers(ignored) { warn!( "libp2p-prefer {preferred_given:?} has libp2p-ignore {ignored_given:?} in it: addresses there stay left out" ); } } } Ok(()) } impl Libp2pConfig { /// The configuration `settings` give; `relay_only`: a relay that is no node /// (`--relay-only`). pub(crate) fn from_settings( settings: &crate::config::Settings, relay_only: bool, ) -> Result { let parse = parse_addresses; Ok(Self { key: Self::load_key( settings.libp2p_key_file.as_deref(), settings.libp2p_key_create, )?, listen: parse("libp2p-listen", &settings.libp2p_listen)?, announce: parse("libp2p-announce", &settings.libp2p_announce)?, relays: parse("libp2p-relays", &settings.libp2p_relays)?, relay: settings.libp2p_relay || relay_only, continuum: !relay_only, mdns: settings.libp2p_mdns, upnp: settings.libp2p_upnp, ignore: parse_ranges("libp2p-ignore", &settings.libp2p_ignore)?, prefer: parse_ranges("libp2p-prefer", &settings.libp2p_prefer)?, internet_hosts: settings.libp2p_internet_hosts.clone(), public: settings.libp2p_public, memory: false, keepalive: settings.keepalive, }) } /// The key in `path` (32 raw bytes, an Ed25519 secret key), created there with /// `create` if there is no file; or a new one each start without a path: the peer /// ID only says where a node is, so a new one costs nothing but the WHO that finds /// it again (`transport::identity`). pub(crate) fn load_key(path: Option<&Path>, create: bool) -> Result { let Some(path) = path else { return Ok(Keypair::generate_ed25519()); }; let (mut bytes, created) = crate::files::key_file( path, "libp2p key", create, "libp2p-key-create", "a new peer ID", 32, )?; if created { info!(path = %path.display(), "Created a new libp2p key"); } if bytes.len() != 32 { bail!( "The libp2p key in {} has {} bytes; it has 32", path.display(), bytes.len() ); } Keypair::ed25519_from_bytes(&mut bytes) .map_err(|error| anyhow!("The libp2p key in {}: {error}", path.display())) } }