diff --git a/Cargo.lock b/Cargo.lock index 2c52ee034..a7284c352 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4382,6 +4382,7 @@ dependencies = [ "thiserror", "tokio 0.3.6", "tokio-util 0.5.1", + "toml", "tower", "tracing", "tracing-error", diff --git a/zebra-network/Cargo.toml b/zebra-network/Cargo.toml index a9fb29b72..d3729a8bb 100644 --- a/zebra-network/Cargo.toml +++ b/zebra-network/Cargo.toml @@ -38,5 +38,6 @@ zebra-chain = { path = "../zebra-chain" } [dev-dependencies] proptest = "0.10" proptest-derive = "0.3" +toml = "0.5" zebra-test = { path = "../zebra-test/" } diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index e007c3bee..6a4f3e488 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -1,4 +1,11 @@ -use std::{collections::HashSet, net::SocketAddr, string::String, time::Duration}; +use std::{ + collections::HashSet, + net::{IpAddr, SocketAddr}, + string::String, + time::Duration, +}; + +use serde::{de, Deserialize, Deserializer}; use zebra_chain::parameters::Network; @@ -9,11 +16,15 @@ use crate::BoxError; const MAX_SINGLE_PEER_RETRIES: usize = 2; /// Configuration for networking code. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields, default)] +#[derive(Clone, Debug, Serialize)] pub struct Config { /// The address on which this node should listen for connections. /// + /// Can be `address:port` or just `address`. If there is no configured + /// port, Zebra will use the default port for the configured `network`. + /// `address` can be an IP address or a DNS name. DNS names are + /// only resolved once, when Zebra starts up. + /// /// Zebra will also advertise this address to other nodes. Advertising a /// different external IP address is currently not supported, see #1890 /// for details. @@ -44,7 +55,6 @@ pub struct Config { /// - regularly, every time `crawl_new_peer_interval` elapses, and /// - if the peer set is busy, and there aren't any peer addresses for the /// next connection attempt. - #[serde(alias = "new_peer_interval")] pub crawl_new_peer_interval: Duration, } @@ -176,3 +186,61 @@ impl Default for Config { } } } + +impl<'de> Deserialize<'de> for Config { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(deny_unknown_fields, default)] + struct DConfig { + listen_addr: String, + network: Network, + initial_mainnet_peers: HashSet, + initial_testnet_peers: HashSet, + peerset_initial_target_size: usize, + #[serde(alias = "new_peer_interval")] + crawl_new_peer_interval: Duration, + } + + impl Default for DConfig { + fn default() -> Self { + let config = Config::default(); + Self { + listen_addr: config.listen_addr.to_string(), + network: config.network, + initial_mainnet_peers: config.initial_mainnet_peers, + initial_testnet_peers: config.initial_testnet_peers, + peerset_initial_target_size: config.peerset_initial_target_size, + crawl_new_peer_interval: config.crawl_new_peer_interval, + } + } + } + + let config = DConfig::deserialize(deserializer)?; + // TODO: perform listener DNS lookups asynchronously with a timeout (#1631) + let listen_addr = match config.listen_addr.parse::() { + Ok(socket) => Ok(socket), + Err(_) => match config.listen_addr.parse::() { + Ok(ip) => Ok(SocketAddr::new(ip, config.network.default_port())), + Err(err) => Err(de::Error::custom(format!( + "{}; Hint: addresses can be a IPv4, IPv6 (with brackets), or a DNS name, the port is optional", + err + ))), + }, + }?; + + Ok(Config { + listen_addr, + network: config.network, + initial_mainnet_peers: config.initial_mainnet_peers, + initial_testnet_peers: config.initial_testnet_peers, + peerset_initial_target_size: config.peerset_initial_target_size, + crawl_new_peer_interval: config.crawl_new_peer_interval, + }) + } +} + +#[cfg(test)] +mod tests; diff --git a/zebra-network/src/config/tests.rs b/zebra-network/src/config/tests.rs new file mode 100644 index 000000000..0639e0699 --- /dev/null +++ b/zebra-network/src/config/tests.rs @@ -0,0 +1,22 @@ +use super::Config; + +#[test] +fn parse_config_listen_addr() { + let fixtures = vec![ + ("listen_addr = '0.0.0.0'", "0.0.0.0:8233"), + ("listen_addr = '0.0.0.0:9999'", "0.0.0.0:9999"), + ( + "listen_addr = '0.0.0.0'\nnetwork = 'Testnet'", + "0.0.0.0:18233", + ), + ( + "listen_addr = '0.0.0.0:8233'\nnetwork = 'Testnet'", + "0.0.0.0:8233", + ), + ]; + + for (config, value) in fixtures { + let config: Config = toml::from_str(config).unwrap(); + assert_eq!(config.listen_addr.to_string(), value); + } +} diff --git a/zebra-network/src/peer_set/initialize.rs b/zebra-network/src/peer_set/initialize.rs index b97cc5aaf..44abfff2d 100644 --- a/zebra-network/src/peer_set/initialize.rs +++ b/zebra-network/src/peer_set/initialize.rs @@ -121,13 +121,12 @@ where // 1. Incoming peer connections, via a listener. // Warn if we're configured using the wrong network port. - // TODO: use the right port if the port is unspecified - // split the address and port configs? - let (wrong_net, wrong_net_port) = match config.network { - Network::Mainnet => (Network::Testnet, 18233), - Network::Testnet => (Network::Mainnet, 8233), + use Network::*; + let wrong_net = match config.network { + Mainnet => Testnet, + Testnet => Mainnet, }; - if config.listen_addr.port() == wrong_net_port { + if config.listen_addr.port() == wrong_net.default_port() { warn!( "We are configured with port {} for {:?}, but that port is the default port for {:?}", config.listen_addr.port(),