1use crate::{
20 arg_enums::{NetworkBackendType, SyncMode},
21 params::node_key_params::NodeKeyParams,
22};
23use clap::Args;
24use sc_network::{
25 config::{
26 NetworkConfiguration, NodeKeyConfig, NonReservedPeerMode, SetConfig, TransportConfig,
27 DEFAULT_IDLE_CONNECTION_TIMEOUT,
28 },
29 multiaddr::Protocol,
30};
31use sc_service::{
32 config::{Multiaddr, MultiaddrWithPeerId},
33 ChainSpec, ChainType,
34};
35use std::{borrow::Cow, num::NonZeroUsize, path::PathBuf};
36
37#[derive(Debug, Clone, Args)]
39pub struct NetworkParams {
40 #[arg(long, value_name = "ADDR", num_args = 1..)]
42 pub bootnodes: Vec<MultiaddrWithPeerId>,
43
44 #[arg(long, value_name = "ADDR", num_args = 1..)]
46 pub reserved_nodes: Vec<MultiaddrWithPeerId>,
47
48 #[arg(long)]
56 pub reserved_only: bool,
57
58 #[arg(long, value_name = "PUBLIC_ADDR", num_args = 1..)]
66 pub public_addr: Vec<Multiaddr>,
67
68 #[arg(long, value_name = "LISTEN_ADDR", num_args = 1..)]
79 pub listen_addr: Vec<Multiaddr>,
80
81 #[arg(long)]
87 pub force_enable_webrtc: bool,
88
89 #[arg(long, value_name = "PORT", conflicts_with_all = &[ "listen_addr" ])]
91 pub port: Option<u16>,
92
93 #[arg(long, alias = "no-private-ipv4", conflicts_with_all = &["allow_private_ip"])]
102 pub no_private_ip: bool,
103
104 #[arg(long, alias = "allow-private-ipv4", conflicts_with_all = &["no_private_ip"])]
112 pub allow_private_ip: bool,
113
114 #[arg(long, value_name = "COUNT", default_value_t = 8)]
116 pub out_peers: u32,
117
118 #[arg(long, value_name = "COUNT", default_value_t = 32)]
120 pub in_peers: u32,
121
122 #[arg(long, value_name = "COUNT", default_value_t = 500)]
124 pub in_peers_light: u32,
125
126 #[arg(long)]
131 pub no_mdns: bool,
132
133 #[arg(long, value_name = "COUNT", default_value_t = 5)]
138 pub max_parallel_downloads: u32,
139
140 #[allow(missing_docs)]
141 #[clap(flatten)]
142 pub node_key_params: NodeKeyParams,
143
144 #[arg(long)]
149 pub discover_local: bool,
150
151 #[arg(long)]
158 pub kademlia_disjoint_query_paths: bool,
159
160 #[arg(long, default_value = "20")]
167 pub kademlia_replication_factor: NonZeroUsize,
168
169 #[arg(long)]
171 pub ipfs_server: bool,
172
173 #[arg(long, value_name = "ADDR", num_args = 1.., requires = "ipfs_server")]
175 pub ipfs_bootnodes: Vec<MultiaddrWithPeerId>,
176
177 #[arg(
179 long,
180 value_enum,
181 value_name = "SYNC_MODE",
182 default_value_t = SyncMode::Full,
183 ignore_case = true,
184 verbatim_doc_comment
185 )]
186 pub sync: SyncMode,
187
188 #[arg(long, value_name = "COUNT", default_value_t = 64)]
193 pub max_blocks_per_request: u32,
194
195 #[arg(
204 long,
205 value_enum,
206 value_name = "NETWORK_BACKEND",
207 default_value_t = NetworkBackendType::Litep2p,
208 ignore_case = true,
209 verbatim_doc_comment
210 )]
211 pub network_backend: NetworkBackendType,
212}
213
214impl NetworkParams {
215 pub fn network_config(
217 &self,
218 chain_spec: &Box<dyn ChainSpec>,
219 is_dev: bool,
220 is_validator: bool,
221 net_config_path: Option<PathBuf>,
222 client_id: &str,
223 node_name: &str,
224 node_key: NodeKeyConfig,
225 default_listen_port: u16,
226 ) -> NetworkConfiguration {
227 let port = self.port.unwrap_or(default_listen_port);
228
229 if self.force_enable_webrtc && !matches!(self.network_backend, NetworkBackendType::Litep2p)
230 {
231 log::warn!(
232 "`--force-enable-webrtc` has no effect: WebRTC is only supported by the litep2p \
233 network backend",
234 );
235 }
236
237 let listen_addresses = if self.listen_addr.is_empty() {
238 let mut listen_addresses = if is_validator || is_dev {
239 vec![
240 Multiaddr::empty()
241 .with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
242 .with(Protocol::Tcp(port)),
243 Multiaddr::empty()
244 .with(Protocol::Ip4([0, 0, 0, 0].into()))
245 .with(Protocol::Tcp(port)),
246 ]
247 } else {
248 vec![
249 Multiaddr::empty()
250 .with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
251 .with(Protocol::Tcp(port))
252 .with(Protocol::Ws(Cow::Borrowed("/"))),
253 Multiaddr::empty()
254 .with(Protocol::Ip4([0, 0, 0, 0].into()))
255 .with(Protocol::Tcp(port))
256 .with(Protocol::Ws(Cow::Borrowed("/"))),
257 ]
258 };
259
260 if matches!(self.network_backend, NetworkBackendType::Litep2p) &&
261 (self.force_enable_webrtc || !is_validator)
262 {
263 listen_addresses.extend([
264 Multiaddr::empty()
265 .with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
266 .with(Protocol::Udp(port))
267 .with(Protocol::WebRTCDirect),
268 Multiaddr::empty()
269 .with(Protocol::Ip4([0, 0, 0, 0].into()))
270 .with(Protocol::Udp(port))
271 .with(Protocol::WebRTCDirect),
272 ]);
273 }
274
275 listen_addresses
276 } else {
277 self.listen_addr.clone()
278 };
279
280 let public_addresses = self.public_addr.clone();
281
282 let mut boot_nodes = chain_spec.boot_nodes().to_vec();
283 boot_nodes.extend(self.bootnodes.clone());
284
285 let chain_type = chain_spec.chain_type();
286 let allow_non_globals_in_dht =
289 self.discover_local ||
290 is_dev || matches!(chain_type, ChainType::Local | ChainType::Development);
291
292 let allow_private_ip = match (self.allow_private_ip, self.no_private_ip) {
293 (true, true) => unreachable!("`*_private_ip` flags are mutually exclusive; qed"),
294 (true, false) => true,
295 (false, true) => false,
296 (false, false) => {
297 is_dev || matches!(chain_type, ChainType::Local | ChainType::Development)
298 },
299 };
300
301 NetworkConfiguration {
302 boot_nodes,
303 net_config_path,
304 default_peers_set: SetConfig {
305 in_peers: self.in_peers + self.in_peers_light,
306 out_peers: self.out_peers,
307 reserved_nodes: self.reserved_nodes.clone(),
308 non_reserved_mode: if self.reserved_only {
309 NonReservedPeerMode::Deny
310 } else {
311 NonReservedPeerMode::Accept
312 },
313 },
314 default_peers_set_num_full: self.in_peers + self.out_peers,
315 listen_addresses,
316 public_addresses,
317 node_key,
318 node_name: node_name.to_string(),
319 client_version: client_id.to_string(),
320 transport: TransportConfig::Normal {
321 enable_mdns: !is_dev && !self.no_mdns,
322 allow_private_ip,
323 },
324 idle_connection_timeout: DEFAULT_IDLE_CONNECTION_TIMEOUT,
325 max_parallel_downloads: self.max_parallel_downloads,
326 max_blocks_per_request: self.max_blocks_per_request,
327 min_peers_to_start_warp_sync: None,
328 enable_dht_random_walk: !self.reserved_only,
329 allow_non_globals_in_dht,
330 kademlia_disjoint_query_paths: self.kademlia_disjoint_query_paths,
331 kademlia_replication_factor: self.kademlia_replication_factor,
332 ipfs_server: self.ipfs_server,
333 ipfs_bootnodes: self.ipfs_bootnodes.clone(),
334 sync_mode: self.sync.into(),
335 network_backend: self.network_backend.into(),
336 }
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use clap::Parser;
344
345 #[derive(Parser)]
346 struct Cli {
347 #[clap(flatten)]
348 network_params: NetworkParams,
349 }
350
351 #[test]
352 fn reserved_nodes_multiple_values_and_occurrences() {
353 let params = Cli::try_parse_from([
354 "",
355 "--reserved-nodes",
356 "/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
357 "/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
358 "--reserved-nodes",
359 "/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
360 ])
361 .expect("Parses network params");
362
363 let expected = vec![
364 MultiaddrWithPeerId::try_from(
365 "/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
366 .to_string(),
367 )
368 .unwrap(),
369 MultiaddrWithPeerId::try_from(
370 "/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
371 .to_string(),
372 )
373 .unwrap(),
374 MultiaddrWithPeerId::try_from(
375 "/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
376 .to_string(),
377 )
378 .unwrap(),
379 ];
380
381 assert_eq!(expected, params.network_params.reserved_nodes);
382 }
383
384 #[test]
385 fn sync_ignores_case() {
386 let params = Cli::try_parse_from(["", "--sync", "wArP"]).expect("Parses network params");
387
388 assert_eq!(SyncMode::Warp, params.network_params.sync);
389 }
390}