diff --git a/zebra-network/src/config.rs b/zebra-network/src/config.rs index 23e812f8..49798137 100644 --- a/zebra-network/src/config.rs +++ b/zebra-network/src/config.rs @@ -19,8 +19,8 @@ use zebra_chain::parameters::Network; use crate::{ constants::{ - DEFAULT_CRAWL_NEW_PEER_INTERVAL, DNS_LOOKUP_TIMEOUT, INBOUND_PEER_LIMIT_MULTIPLIER, - MAX_PEER_DISK_CACHE_SIZE, OUTBOUND_PEER_LIMIT_MULTIPLIER, + DEFAULT_CRAWL_NEW_PEER_INTERVAL, DEFAULT_MAX_CONNS_PER_IP, DNS_LOOKUP_TIMEOUT, + INBOUND_PEER_LIMIT_MULTIPLIER, MAX_PEER_DISK_CACHE_SIZE, OUTBOUND_PEER_LIMIT_MULTIPLIER, }, protocol::external::{canonical_peer_addr, canonical_socket_addr}, BoxError, PeerSocketAddr, @@ -153,6 +153,12 @@ pub struct Config { /// next connection attempt. #[serde(with = "humantime_serde")] pub crawl_new_peer_interval: Duration, + + /// The maximum number of peer connections Zebra will keep for a given IP address + /// before it drops any additional peer connections with that IP. + /// + /// The default and minimum value are 1. + pub max_connections_per_ip: usize, } impl Config { @@ -591,6 +597,7 @@ impl Default for Config { // But Zebra should only make a small number of initial outbound connections, // so that idle peers don't use too many connection slots. peerset_initial_target_size: 25, + max_connections_per_ip: DEFAULT_MAX_CONNS_PER_IP, } } } @@ -611,6 +618,7 @@ impl<'de> Deserialize<'de> for Config { peerset_initial_target_size: usize, #[serde(alias = "new_peer_interval", with = "humantime_serde")] crawl_new_peer_interval: Duration, + max_connections_per_ip: Option, } impl Default for DConfig { @@ -624,16 +632,26 @@ impl<'de> Deserialize<'de> for Config { cache_dir: config.cache_dir, peerset_initial_target_size: config.peerset_initial_target_size, crawl_new_peer_interval: config.crawl_new_peer_interval, + max_connections_per_ip: Some(DEFAULT_MAX_CONNS_PER_IP), } } } - let config = DConfig::deserialize(deserializer)?; + let DConfig { + listen_addr, + network, + initial_mainnet_peers, + initial_testnet_peers, + cache_dir, + peerset_initial_target_size, + crawl_new_peer_interval, + max_connections_per_ip, + } = DConfig::deserialize(deserializer)?; - let listen_addr = match config.listen_addr.parse::() { + let listen_addr = match listen_addr.parse::() { Ok(socket) => Ok(socket), - Err(_) => match config.listen_addr.parse::() { - Ok(ip) => Ok(SocketAddr::new(ip, config.network.default_port())), + Err(_) => match listen_addr.parse::() { + Ok(ip) => Ok(SocketAddr::new(ip, network.default_port())), Err(err) => Err(de::Error::custom(format!( "{err}; Hint: addresses can be a IPv4, IPv6 (with brackets), or a DNS name, the port is optional" ))), @@ -642,12 +660,13 @@ impl<'de> Deserialize<'de> for Config { Ok(Config { listen_addr: canonical_socket_addr(listen_addr), - network: config.network, - initial_mainnet_peers: config.initial_mainnet_peers, - initial_testnet_peers: config.initial_testnet_peers, - cache_dir: config.cache_dir, - peerset_initial_target_size: config.peerset_initial_target_size, - crawl_new_peer_interval: config.crawl_new_peer_interval, + network, + initial_mainnet_peers, + initial_testnet_peers, + cache_dir, + peerset_initial_target_size, + crawl_new_peer_interval, + max_connections_per_ip: max_connections_per_ip.unwrap_or(DEFAULT_MAX_CONNS_PER_IP), }) } } diff --git a/zebra-network/src/constants.rs b/zebra-network/src/constants.rs index 289ccd79..b466d4fd 100644 --- a/zebra-network/src/constants.rs +++ b/zebra-network/src/constants.rs @@ -67,9 +67,11 @@ pub const INBOUND_PEER_LIMIT_MULTIPLIER: usize = 5; /// See [`INBOUND_PEER_LIMIT_MULTIPLIER`] for details. pub const OUTBOUND_PEER_LIMIT_MULTIPLIER: usize = 3; -/// The maximum number of peer connections Zebra will keep for a given IP address +/// The default maximum number of peer connections Zebra will keep for a given IP address /// before it drops any additional peer connections with that IP. -pub const MAX_CONNS_PER_IP: usize = 1; +/// +/// This will be used as Config.max_connections_per_ip if no value is provided. +pub const DEFAULT_MAX_CONNS_PER_IP: usize = 1; /// The buffer size for the peer set. /// diff --git a/zebra-network/src/peer_set/set.rs b/zebra-network/src/peer_set/set.rs index 72fdbe79..522ee6b1 100644 --- a/zebra-network/src/peer_set/set.rs +++ b/zebra-network/src/peer_set/set.rs @@ -254,7 +254,7 @@ where last_peer_log: Option, /// The configured maximum number of peers that can be in the - /// peer set per IP, defaults to [`crate::constants::MAX_CONNS_PER_IP`] + /// peer set per IP, defaults to [`crate::constants::DEFAULT_MAX_CONNS_PER_IP`] max_conns_per_ip: usize, } @@ -290,7 +290,8 @@ where /// - `address_book`: when peer set is busy, it logs address book diagnostics. /// - `minimum_peer_version`: endpoint to see the minimum peer protocol version in real time. /// - `max_conns_per_ip`: configured maximum number of peers that can be in the - /// peer set per IP, defaults to [`crate::constants::MAX_CONNS_PER_IP`]. + /// peer set per IP, defaults to the config value or to + /// [`crate::constants::DEFAULT_MAX_CONNS_PER_IP`]. pub fn new( config: &Config, discover: D, @@ -328,7 +329,7 @@ where last_peer_log: None, address_metrics, - max_conns_per_ip: max_conns_per_ip.unwrap_or(crate::constants::MAX_CONNS_PER_IP), + max_conns_per_ip: max_conns_per_ip.unwrap_or(config.max_connections_per_ip), } } @@ -540,7 +541,7 @@ where // # Security // - // drop the new peer if there are already `MAX_CONNS_PER_IP` peers with + // drop the new peer if there are already `max_conns_per_ip` peers with // the same IP address in the peer set. if self.num_peers_with_ip(key.ip()) >= self.max_conns_per_ip { std::mem::drop(svc); diff --git a/zebra-network/src/peer_set/set/tests/vectors.rs b/zebra-network/src/peer_set/set/tests/vectors.rs index c3534aed..82904699 100644 --- a/zebra-network/src/peer_set/set/tests/vectors.rs +++ b/zebra-network/src/peer_set/set/tests/vectors.rs @@ -12,7 +12,7 @@ use zebra_chain::{ use super::{PeerSetBuilder, PeerVersions}; use crate::{ - constants::MAX_CONNS_PER_IP, + constants::DEFAULT_MAX_CONNS_PER_IP, peer::{ClientRequest, MinimumPeerVersion}, peer_set::inventory_registry::InventoryStatus, protocol::external::{types::Version, InventoryHash}, @@ -145,7 +145,7 @@ fn peer_set_ready_multiple_connections() { let (mut peer_set, _peer_set_guard) = PeerSetBuilder::new() .with_discover(discovered_peers) .with_minimum_peer_version(minimum_peer_version.clone()) - .max_conns_per_ip(max(3, MAX_CONNS_PER_IP)) + .max_conns_per_ip(max(3, DEFAULT_MAX_CONNS_PER_IP)) .build(); // Get peerset ready @@ -178,7 +178,7 @@ fn peer_set_ready_multiple_connections() { #[test] fn peer_set_rejects_connections_past_per_ip_limit() { - const NUM_PEER_VERSIONS: usize = crate::constants::MAX_CONNS_PER_IP + 1; + const NUM_PEER_VERSIONS: usize = crate::constants::DEFAULT_MAX_CONNS_PER_IP + 1; // Use three peers with the same version let peer_version = Version::min_specified_for_upgrade(Network::Mainnet, NetworkUpgrade::Nu5); @@ -220,7 +220,7 @@ fn peer_set_rejects_connections_past_per_ip_limit() { // Check we have the right amount of ready services assert_eq!( peer_ready.ready_services.len(), - crate::constants::MAX_CONNS_PER_IP + crate::constants::DEFAULT_MAX_CONNS_PER_IP ); }); } @@ -259,7 +259,7 @@ fn peer_set_route_inv_empty_registry() { let (mut peer_set, _peer_set_guard) = PeerSetBuilder::new() .with_discover(discovered_peers) .with_minimum_peer_version(minimum_peer_version.clone()) - .max_conns_per_ip(max(2, MAX_CONNS_PER_IP)) + .max_conns_per_ip(max(2, DEFAULT_MAX_CONNS_PER_IP)) .build(); // Get peerset ready @@ -342,7 +342,7 @@ fn peer_set_route_inv_advertised_registry_order(advertised_first: bool) { let (mut peer_set, mut peer_set_guard) = PeerSetBuilder::new() .with_discover(discovered_peers) .with_minimum_peer_version(minimum_peer_version.clone()) - .max_conns_per_ip(max(2, MAX_CONNS_PER_IP)) + .max_conns_per_ip(max(2, DEFAULT_MAX_CONNS_PER_IP)) .build(); // Advertise some inventory @@ -450,7 +450,7 @@ fn peer_set_route_inv_missing_registry_order(missing_first: bool) { let (mut peer_set, mut peer_set_guard) = PeerSetBuilder::new() .with_discover(discovered_peers) .with_minimum_peer_version(minimum_peer_version.clone()) - .max_conns_per_ip(max(2, MAX_CONNS_PER_IP)) + .max_conns_per_ip(max(2, DEFAULT_MAX_CONNS_PER_IP)) .build(); // Mark some inventory as missing diff --git a/zebrad/tests/common/configs/getblocktemplate-v1.0.1.toml b/zebrad/tests/common/configs/getblocktemplate-v1.0.1.toml new file mode 100644 index 00000000..3536c80c --- /dev/null +++ b/zebrad/tests/common/configs/getblocktemplate-v1.0.1.toml @@ -0,0 +1,75 @@ +# Default configuration for zebrad. +# +# This file can be used as a skeleton for custom configs. +# +# Unspecified fields use default values. Optional fields are Some(field) if the +# field is present and None if it is absent. +# +# This file is generated as an example using zebrad's current defaults. +# You should set only the config options you want to keep, and delete the rest. +# Only a subset of fields are present in the skeleton, since optional values +# whose default is None are omitted. +# +# The config format (including a complete list of sections and fields) is +# documented here: +# https://doc.zebra.zfnd.org/zebrad/config/struct.ZebradConfig.html +# +# zebrad attempts to load configs in the following order: +# +# 1. The -c flag on the command line, e.g., `zebrad -c myconfig.toml start`; +# 2. The file `zebrad.toml` in the users's preference directory (platform-dependent); +# 3. The default config. + +[consensus] +checkpoint_sync = true +debug_skip_parameter_preload = false + +[mempool] +eviction_memory_time = "1h" +tx_cost_limit = 80000000 + +[metrics] + +[mining] +debug_like_zcashd = true + +[network] +cache_dir = true +crawl_new_peer_interval = "1m 1s" +initial_mainnet_peers = [ + "dnsseed.z.cash:8233", + "dnsseed.str4d.xyz:8233", + "mainnet.seeder.zfnd.org:8233", + "mainnet.is.yolo.money:8233", +] +initial_testnet_peers = [ + "dnsseed.testnet.z.cash:18233", + "testnet.seeder.zfnd.org:18233", + "testnet.is.yolo.money:18233", +] +listen_addr = "0.0.0.0:8233" +max_connections_per_ip = 1 +network = "Mainnet" +peerset_initial_target_size = 25 + +[rpc] +debug_force_finished_sync = false +parallel_cpu_threads = 0 + +[state] +cache_dir = "cache_dir" +delete_old_database = true +ephemeral = false + +[sync] +checkpoint_verify_concurrency_limit = 1000 +download_concurrency_limit = 50 +full_verify_concurrency_limit = 20 +parallel_cpu_threads = 0 + +[tracing] +buffer_limit = 128000 +force_use_color = false +use_color = true +use_journald = false + diff --git a/zebrad/tests/common/configs/v1.0.1.toml b/zebrad/tests/common/configs/v1.0.1.toml new file mode 100644 index 00000000..02bac53d --- /dev/null +++ b/zebrad/tests/common/configs/v1.0.1.toml @@ -0,0 +1,71 @@ +# Default configuration for zebrad. +# +# This file can be used as a skeleton for custom configs. +# +# Unspecified fields use default values. Optional fields are Some(field) if the +# field is present and None if it is absent. +# +# This file is generated as an example using zebrad's current defaults. +# You should set only the config options you want to keep, and delete the rest. +# Only a subset of fields are present in the skeleton, since optional values +# whose default is None are omitted. +# +# The config format (including a complete list of sections and fields) is +# documented here: +# https://doc.zebra.zfnd.org/zebrad/config/struct.ZebradConfig.html +# +# zebrad attempts to load configs in the following order: +# +# 1. The -c flag on the command line, e.g., `zebrad -c myconfig.toml start`; +# 2. The file `zebrad.toml` in the users's preference directory (platform-dependent); +# 3. The default config. + +[consensus] +checkpoint_sync = true +debug_skip_parameter_preload = false + +[mempool] +eviction_memory_time = "1h" +tx_cost_limit = 80000000 + +[metrics] + +[network] +cache_dir = true +crawl_new_peer_interval = "1m 1s" +initial_mainnet_peers = [ + "dnsseed.z.cash:8233", + "dnsseed.str4d.xyz:8233", + "mainnet.seeder.zfnd.org:8233", + "mainnet.is.yolo.money:8233", +] +initial_testnet_peers = [ + "dnsseed.testnet.z.cash:18233", + "testnet.seeder.zfnd.org:18233", + "testnet.is.yolo.money:18233", +] +listen_addr = "0.0.0.0:8233" +max_connections_per_ip = 1 +network = "Mainnet" +peerset_initial_target_size = 25 + +[rpc] +debug_force_finished_sync = false +parallel_cpu_threads = 1 + +[state] +cache_dir = "cache_dir" +delete_old_database = true +ephemeral = false + +[sync] +checkpoint_verify_concurrency_limit = 1000 +download_concurrency_limit = 50 +full_verify_concurrency_limit = 20 +parallel_cpu_threads = 0 + +[tracing] +buffer_limit = 128000 +force_use_color = false +use_color = true +use_journald = false