1use crate::{
20 arg_enums::{NetworkBackendType, SyncMode},
21 params::node_key_params::NodeKeyParams,
22};
23use clap::Args;
24use sc_network::{
25 config::{
26 default_light_request_call_workers, NetworkConfiguration, NodeKeyConfig,
27 NonReservedPeerMode, SetConfig, TransportConfig, DEFAULT_IDLE_CONNECTION_TIMEOUT,
28 DEFAULT_LIGHT_REQUEST_EXECUTION_TIMEOUT,
29 },
30 multiaddr::Protocol,
31};
32use sc_service::{
33 config::{Multiaddr, MultiaddrWithPeerId},
34 ChainSpec, ChainType,
35};
36use std::{borrow::Cow, num::NonZeroUsize, path::PathBuf, time::Duration};
37
38#[derive(Debug, Clone, Args)]
40pub struct NetworkParams {
41 #[arg(long, value_name = "ADDR", num_args = 1..)]
43 pub bootnodes: Vec<MultiaddrWithPeerId>,
44
45 #[arg(long, value_name = "ADDR", num_args = 1..)]
47 pub reserved_nodes: Vec<MultiaddrWithPeerId>,
48
49 #[arg(long)]
57 pub reserved_only: bool,
58
59 #[arg(long, value_name = "PUBLIC_ADDR", num_args = 1..)]
71 pub public_addr: Vec<Multiaddr>,
72
73 #[arg(long, value_name = "LISTEN_ADDR", num_args = 1..)]
88 pub listen_addr: Vec<Multiaddr>,
89
90 #[arg(long)]
96 pub force_enable_webrtc: bool,
97
98 #[arg(long, value_name = "PORT", conflicts_with_all = &[ "listen_addr" ])]
100 pub port: Option<u16>,
101
102 #[arg(long, alias = "no-private-ipv4", conflicts_with_all = &["allow_private_ip"])]
111 pub no_private_ip: bool,
112
113 #[arg(long, alias = "allow-private-ipv4", conflicts_with_all = &["no_private_ip"])]
121 pub allow_private_ip: bool,
122
123 #[arg(long, value_name = "COUNT", default_value_t = 8)]
125 pub out_peers: u32,
126
127 #[arg(long, value_name = "COUNT", default_value_t = 32)]
129 pub in_peers: u32,
130
131 #[arg(long, value_name = "COUNT", default_value_t = 500)]
133 pub in_peers_light: u32,
134
135 #[arg(long)]
140 pub no_mdns: bool,
141
142 #[arg(long, value_name = "COUNT", default_value_t = 5)]
147 pub max_parallel_downloads: u32,
148
149 #[allow(missing_docs)]
150 #[clap(flatten)]
151 pub node_key_params: NodeKeyParams,
152
153 #[arg(long)]
158 pub discover_local: bool,
159
160 #[arg(long)]
167 pub kademlia_disjoint_query_paths: bool,
168
169 #[arg(long, default_value = "20")]
176 pub kademlia_replication_factor: NonZeroUsize,
177
178 #[arg(long)]
180 pub ipfs_server: bool,
181
182 #[arg(long, value_name = "ADDR", num_args = 1.., requires = "ipfs_server")]
184 pub ipfs_bootnodes: Vec<MultiaddrWithPeerId>,
185
186 #[arg(
195 long,
196 value_name = "MILLISECONDS",
197 default_value_t = DEFAULT_LIGHT_REQUEST_EXECUTION_TIMEOUT.as_millis() as u64
198 )]
199 pub light_request_execution_timeout_ms: u64,
200
201 #[arg(long, value_name = "COUNT")]
206 pub light_request_call_workers: Option<NonZeroUsize>,
207
208 #[arg(
210 long,
211 value_enum,
212 value_name = "SYNC_MODE",
213 default_value_t = SyncMode::Full,
214 ignore_case = true,
215 verbatim_doc_comment
216 )]
217 pub sync: SyncMode,
218
219 #[arg(long, value_name = "COUNT", default_value_t = 64)]
224 pub max_blocks_per_request: u32,
225
226 #[arg(
235 long,
236 value_enum,
237 value_name = "NETWORK_BACKEND",
238 default_value_t = NetworkBackendType::Litep2p,
239 ignore_case = true,
240 verbatim_doc_comment
241 )]
242 pub network_backend: NetworkBackendType,
243}
244
245impl NetworkParams {
246 pub fn network_config(
248 &self,
249 chain_spec: &Box<dyn ChainSpec>,
250 is_dev: bool,
251 is_validator: bool,
252 net_config_path: Option<PathBuf>,
253 client_id: &str,
254 node_name: &str,
255 node_key: NodeKeyConfig,
256 default_listen_port: u16,
257 ) -> NetworkConfiguration {
258 let port = self.port.unwrap_or(default_listen_port);
259
260 if self.force_enable_webrtc && !matches!(self.network_backend, NetworkBackendType::Litep2p)
261 {
262 log::warn!(
263 "`--force-enable-webrtc` has no effect: WebRTC is only supported by the litep2p \
264 network backend",
265 );
266 }
267
268 let listen_addresses = if self.listen_addr.is_empty() {
269 let mut listen_addresses = if is_validator || is_dev {
270 vec![
271 Multiaddr::empty()
272 .with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
273 .with(Protocol::Tcp(port)),
274 Multiaddr::empty()
275 .with(Protocol::Ip4([0, 0, 0, 0].into()))
276 .with(Protocol::Tcp(port)),
277 ]
278 } else {
279 vec![
280 Multiaddr::empty()
281 .with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
282 .with(Protocol::Tcp(port))
283 .with(Protocol::Ws(Cow::Borrowed("/"))),
284 Multiaddr::empty()
285 .with(Protocol::Ip4([0, 0, 0, 0].into()))
286 .with(Protocol::Tcp(port))
287 .with(Protocol::Ws(Cow::Borrowed("/"))),
288 ]
289 };
290
291 if matches!(self.network_backend, NetworkBackendType::Litep2p) &&
292 (self.force_enable_webrtc || !is_validator)
293 {
294 listen_addresses.extend([
295 Multiaddr::empty()
296 .with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
297 .with(Protocol::Udp(port))
298 .with(Protocol::WebRTCDirect),
299 Multiaddr::empty()
300 .with(Protocol::Ip4([0, 0, 0, 0].into()))
301 .with(Protocol::Udp(port))
302 .with(Protocol::WebRTCDirect),
303 ]);
304 }
305
306 listen_addresses
307 } else {
308 self.listen_addr.clone()
309 };
310
311 let public_addresses = self.public_addr.clone();
312
313 let mut boot_nodes = chain_spec.boot_nodes().to_vec();
314 boot_nodes.extend(self.bootnodes.clone());
315
316 let chain_type = chain_spec.chain_type();
317 let allow_non_globals_in_dht =
320 self.discover_local ||
321 is_dev || matches!(chain_type, ChainType::Local | ChainType::Development);
322
323 let allow_private_ip = match (self.allow_private_ip, self.no_private_ip) {
324 (true, true) => unreachable!("`*_private_ip` flags are mutually exclusive; qed"),
325 (true, false) => true,
326 (false, true) => false,
327 (false, false) => {
328 is_dev || matches!(chain_type, ChainType::Local | ChainType::Development)
329 },
330 };
331
332 NetworkConfiguration {
333 boot_nodes,
334 net_config_path,
335 default_peers_set: SetConfig {
336 in_peers: self.in_peers + self.in_peers_light,
337 out_peers: self.out_peers,
338 reserved_nodes: self.reserved_nodes.clone(),
339 non_reserved_mode: if self.reserved_only {
340 NonReservedPeerMode::Deny
341 } else {
342 NonReservedPeerMode::Accept
343 },
344 },
345 default_peers_set_num_full: self.in_peers + self.out_peers,
346 listen_addresses,
347 public_addresses,
348 node_key,
349 node_name: node_name.to_string(),
350 client_version: client_id.to_string(),
351 transport: TransportConfig::Normal {
352 enable_mdns: !is_dev && !self.no_mdns,
353 allow_private_ip,
354 },
355 idle_connection_timeout: DEFAULT_IDLE_CONNECTION_TIMEOUT,
356 max_parallel_downloads: self.max_parallel_downloads,
357 max_blocks_per_request: self.max_blocks_per_request,
358 min_peers_to_start_warp_sync: None,
359 enable_dht_random_walk: !self.reserved_only,
360 allow_non_globals_in_dht,
361 kademlia_disjoint_query_paths: self.kademlia_disjoint_query_paths,
362 kademlia_replication_factor: self.kademlia_replication_factor,
363 ipfs_server: self.ipfs_server,
364 ipfs_bootnodes: self.ipfs_bootnodes.clone(),
365 light_request_execution_timeout: match self.light_request_execution_timeout_ms {
366 0 => None,
367 ms => Some(Duration::from_millis(ms)),
368 },
369 light_request_call_workers: self
370 .light_request_call_workers
371 .unwrap_or_else(default_light_request_call_workers),
372 sync_mode: self.sync.into(),
373 network_backend: self.network_backend.into(),
374 }
375 }
376}
377
378#[cfg(test)]
379mod tests {
380 use super::*;
381 use clap::Parser;
382
383 #[derive(Parser)]
384 struct Cli {
385 #[clap(flatten)]
386 network_params: NetworkParams,
387 }
388
389 #[test]
390 fn reserved_nodes_multiple_values_and_occurrences() {
391 let params = Cli::try_parse_from([
392 "",
393 "--reserved-nodes",
394 "/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
395 "/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
396 "--reserved-nodes",
397 "/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
398 ])
399 .expect("Parses network params");
400
401 let expected = vec![
402 MultiaddrWithPeerId::try_from(
403 "/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
404 .to_string(),
405 )
406 .unwrap(),
407 MultiaddrWithPeerId::try_from(
408 "/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
409 .to_string(),
410 )
411 .unwrap(),
412 MultiaddrWithPeerId::try_from(
413 "/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
414 .to_string(),
415 )
416 .unwrap(),
417 ];
418
419 assert_eq!(expected, params.network_params.reserved_nodes);
420 }
421
422 #[test]
423 fn sync_ignores_case() {
424 let params = Cli::try_parse_from(["", "--sync", "wArP"]).expect("Parses network params");
425
426 assert_eq!(SyncMode::Warp, params.network_params.sync);
427 }
428
429 #[test]
430 fn light_request_call_workers_must_be_non_zero() {
431 let parse = |workers: &str| {
432 Cli::try_parse_from(["", "--light-request-call-workers", workers])
433 .map(|cli| cli.network_params.light_request_call_workers)
434 };
435
436 assert_eq!(parse("100").unwrap(), NonZeroUsize::new(100));
437 assert!(parse("0").is_err());
438 }
439}