//! 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, /// 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 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)?; parse_ranges("libp2p-ignore", &settings.libp2p_ignore)?; parse_ranges("libp2p-prefer", &settings.libp2p_prefer)?; 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`). 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())?, 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)?, public: settings.libp2p_public, memory: false, }) } /// The key in `path` (32 raw bytes, an Ed25519 secret key), or a new one each start /// without a file: 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>) -> Result { let Some(path) = path else { return Ok(Keypair::generate_ed25519()); }; let mut bytes = std::fs::read(path) .with_context(|| format!("Reading the libp2p key from {}", path.display()))?; crate::files::warn_if_readable_by_others(path, "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())) } }