sc_cli/params/
network_params.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use 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	},
28	multiaddr::Protocol,
29};
30use sc_service::{
31	config::{Multiaddr, MultiaddrWithPeerId},
32	ChainSpec, ChainType,
33};
34use std::{borrow::Cow, num::NonZeroUsize, path::PathBuf};
35
36/// Parameters used to create the network configuration.
37#[derive(Debug, Clone, Args)]
38pub struct NetworkParams {
39	/// Specify a list of bootnodes.
40	#[arg(long, value_name = "ADDR", num_args = 1..)]
41	pub bootnodes: Vec<MultiaddrWithPeerId>,
42
43	/// Specify a list of reserved node addresses.
44	#[arg(long, value_name = "ADDR", num_args = 1..)]
45	pub reserved_nodes: Vec<MultiaddrWithPeerId>,
46
47	/// Whether to only synchronize the chain with reserved nodes.
48	///
49	/// Also disables automatic peer discovery.
50	/// TCP connections might still be established with non-reserved nodes.
51	/// In particular, if you are a validator your node might still connect to other
52	/// validator nodes and collator nodes regardless of whether they are defined as
53	/// reserved nodes.
54	#[arg(long)]
55	pub reserved_only: bool,
56
57	/// Public address that other nodes will use to connect to this node.
58	///
59	/// This can be used if there's a proxy in front of this node.
60	#[arg(long, value_name = "PUBLIC_ADDR", num_args = 1..)]
61	pub public_addr: Vec<Multiaddr>,
62
63	/// Listen on this multiaddress.
64	///
65	/// By default:
66	/// If `--validator` is passed: `/ip4/0.0.0.0/tcp/<port>` and `/ip6/[::]/tcp/<port>`.
67	/// Otherwise: `/ip4/0.0.0.0/tcp/<port>/ws` and `/ip6/[::]/tcp/<port>/ws`.
68	#[arg(long, value_name = "LISTEN_ADDR", num_args = 1..)]
69	pub listen_addr: Vec<Multiaddr>,
70
71	/// Specify p2p protocol TCP port.
72	#[arg(long, value_name = "PORT", conflicts_with_all = &[ "listen_addr" ])]
73	pub port: Option<u16>,
74
75	/// Always forbid connecting to private IPv4/IPv6 addresses.
76	///
77	/// The option doesn't apply to addresses passed with `--reserved-nodes` or
78	/// `--bootnodes`. Enabled by default for chains marked as "live" in their chain
79	/// specifications.
80	///
81	/// Address allocation for private networks is specified by
82	/// [RFC1918](https://tools.ietf.org/html/rfc1918)).
83	#[arg(long, alias = "no-private-ipv4", conflicts_with_all = &["allow_private_ip"])]
84	pub no_private_ip: bool,
85
86	/// Always accept connecting to private IPv4/IPv6 addresses.
87	///
88	/// Enabled by default for chains marked as "local" in their chain specifications,
89	/// or when `--dev` is passed.
90	///
91	/// Address allocation for private networks is specified by
92	/// [RFC1918](https://tools.ietf.org/html/rfc1918)).
93	#[arg(long, alias = "allow-private-ipv4", conflicts_with_all = &["no_private_ip"])]
94	pub allow_private_ip: bool,
95
96	/// Number of outgoing connections we're trying to maintain.
97	#[arg(long, value_name = "COUNT", default_value_t = 8)]
98	pub out_peers: u32,
99
100	/// Maximum number of inbound full nodes peers.
101	#[arg(long, value_name = "COUNT", default_value_t = 32)]
102	pub in_peers: u32,
103
104	/// Maximum number of inbound light nodes peers.
105	#[arg(long, value_name = "COUNT", default_value_t = 100)]
106	pub in_peers_light: u32,
107
108	/// Disable mDNS discovery (default: true).
109	///
110	/// By default, the network will use mDNS to discover other nodes on the
111	/// local network. This disables it. Automatically implied when using --dev.
112	#[arg(long)]
113	pub no_mdns: bool,
114
115	/// Maximum number of peers from which to ask for the same blocks in parallel.
116	///
117	/// This allows downloading announced blocks from multiple peers.
118	/// Decrease to save traffic and risk increased latency.
119	#[arg(long, value_name = "COUNT", default_value_t = 5)]
120	pub max_parallel_downloads: u32,
121
122	#[allow(missing_docs)]
123	#[clap(flatten)]
124	pub node_key_params: NodeKeyParams,
125
126	/// Enable peer discovery on local networks.
127	///
128	/// By default this option is `true` for `--dev` or when the chain type is
129	/// `Local`/`Development` and false otherwise.
130	#[arg(long)]
131	pub discover_local: bool,
132
133	/// Require iterative Kademlia DHT queries to use disjoint paths.
134	///
135	/// Disjoint paths increase resiliency in the presence of potentially adversarial nodes.
136	///
137	/// See the S/Kademlia paper for more information on the high level design as well as its
138	/// security improvements.
139	#[arg(long)]
140	pub kademlia_disjoint_query_paths: bool,
141
142	/// Kademlia replication factor.
143	///
144	/// Determines to how many closest peers a record is replicated to.
145	///
146	/// Discovery mechanism requires successful replication to all
147	/// `kademlia_replication_factor` peers to consider record successfully put.
148	#[arg(long, default_value = "20")]
149	pub kademlia_replication_factor: NonZeroUsize,
150
151	/// Join the IPFS network and serve transactions over bitswap protocol.
152	#[arg(long)]
153	pub ipfs_server: bool,
154
155	/// Blockchain syncing mode.
156	#[arg(
157		long,
158		value_enum,
159		value_name = "SYNC_MODE",
160		default_value_t = SyncMode::Full,
161		ignore_case = true,
162		verbatim_doc_comment
163	)]
164	pub sync: SyncMode,
165
166	/// Maximum number of blocks per request.
167	///
168	/// Try reducing this number from the default value if you have a slow network connection
169	/// and observe block requests timing out.
170	#[arg(long, value_name = "COUNT", default_value_t = 64)]
171	pub max_blocks_per_request: u32,
172
173	/// Network backend used for P2P networking.
174	///
175	/// Litep2p is a lightweight alternative to libp2p, that is designed to be more
176	/// efficient and easier to use. At the same time, litep2p brings performance
177	/// improvements and reduces the CPU usage significantly.
178	///
179	/// Libp2p is the old network backend, that may still be used for compatibility
180	/// reasons until the whole ecosystem is migrated to litep2p.
181	#[arg(
182		long,
183		value_enum,
184		value_name = "NETWORK_BACKEND",
185		default_value_t = NetworkBackendType::Litep2p,
186		ignore_case = true,
187		verbatim_doc_comment
188	)]
189	pub network_backend: NetworkBackendType,
190}
191
192impl NetworkParams {
193	/// Fill the given `NetworkConfiguration` by looking at the cli parameters.
194	pub fn network_config(
195		&self,
196		chain_spec: &Box<dyn ChainSpec>,
197		is_dev: bool,
198		is_validator: bool,
199		net_config_path: Option<PathBuf>,
200		client_id: &str,
201		node_name: &str,
202		node_key: NodeKeyConfig,
203		default_listen_port: u16,
204	) -> NetworkConfiguration {
205		let port = self.port.unwrap_or(default_listen_port);
206
207		let listen_addresses = if self.listen_addr.is_empty() {
208			if is_validator || is_dev {
209				vec![
210					Multiaddr::empty()
211						.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
212						.with(Protocol::Tcp(port)),
213					Multiaddr::empty()
214						.with(Protocol::Ip4([0, 0, 0, 0].into()))
215						.with(Protocol::Tcp(port)),
216				]
217			} else {
218				vec![
219					Multiaddr::empty()
220						.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
221						.with(Protocol::Tcp(port))
222						.with(Protocol::Ws(Cow::Borrowed("/"))),
223					Multiaddr::empty()
224						.with(Protocol::Ip4([0, 0, 0, 0].into()))
225						.with(Protocol::Tcp(port))
226						.with(Protocol::Ws(Cow::Borrowed("/"))),
227				]
228			}
229		} else {
230			self.listen_addr.clone()
231		};
232
233		let public_addresses = self.public_addr.clone();
234
235		let mut boot_nodes = chain_spec.boot_nodes().to_vec();
236		boot_nodes.extend(self.bootnodes.clone());
237
238		let chain_type = chain_spec.chain_type();
239		// Activate if the user explicitly requested local discovery, `--dev` is given or the
240		// chain type is `Local`/`Development`
241		let allow_non_globals_in_dht =
242			self.discover_local ||
243				is_dev || matches!(chain_type, ChainType::Local | ChainType::Development);
244
245		let allow_private_ip = match (self.allow_private_ip, self.no_private_ip) {
246			(true, true) => unreachable!("`*_private_ip` flags are mutually exclusive; qed"),
247			(true, false) => true,
248			(false, true) => false,
249			(false, false) =>
250				is_dev || matches!(chain_type, ChainType::Local | ChainType::Development),
251		};
252
253		NetworkConfiguration {
254			boot_nodes,
255			net_config_path,
256			default_peers_set: SetConfig {
257				in_peers: self.in_peers + self.in_peers_light,
258				out_peers: self.out_peers,
259				reserved_nodes: self.reserved_nodes.clone(),
260				non_reserved_mode: if self.reserved_only {
261					NonReservedPeerMode::Deny
262				} else {
263					NonReservedPeerMode::Accept
264				},
265			},
266			default_peers_set_num_full: self.in_peers + self.out_peers,
267			listen_addresses,
268			public_addresses,
269			node_key,
270			node_name: node_name.to_string(),
271			client_version: client_id.to_string(),
272			transport: TransportConfig::Normal {
273				enable_mdns: !is_dev && !self.no_mdns,
274				allow_private_ip,
275			},
276			max_parallel_downloads: self.max_parallel_downloads,
277			max_blocks_per_request: self.max_blocks_per_request,
278			min_peers_to_start_warp_sync: None,
279			enable_dht_random_walk: !self.reserved_only,
280			allow_non_globals_in_dht,
281			kademlia_disjoint_query_paths: self.kademlia_disjoint_query_paths,
282			kademlia_replication_factor: self.kademlia_replication_factor,
283			ipfs_server: self.ipfs_server,
284			sync_mode: self.sync.into(),
285			network_backend: self.network_backend.into(),
286		}
287	}
288}
289
290#[cfg(test)]
291mod tests {
292	use super::*;
293	use clap::Parser;
294
295	#[derive(Parser)]
296	struct Cli {
297		#[clap(flatten)]
298		network_params: NetworkParams,
299	}
300
301	#[test]
302	fn reserved_nodes_multiple_values_and_occurrences() {
303		let params = Cli::try_parse_from([
304			"",
305			"--reserved-nodes",
306			"/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
307			"/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
308			"--reserved-nodes",
309			"/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
310		])
311		.expect("Parses network params");
312
313		let expected = vec![
314			MultiaddrWithPeerId::try_from(
315				"/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
316					.to_string(),
317			)
318			.unwrap(),
319			MultiaddrWithPeerId::try_from(
320				"/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
321					.to_string(),
322			)
323			.unwrap(),
324			MultiaddrWithPeerId::try_from(
325				"/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
326					.to_string(),
327			)
328			.unwrap(),
329		];
330
331		assert_eq!(expected, params.network_params.reserved_nodes);
332	}
333
334	#[test]
335	fn sync_ignores_case() {
336		let params = Cli::try_parse_from(["", "--sync", "wArP"]).expect("Parses network params");
337
338		assert_eq!(SyncMode::Warp, params.network_params.sync);
339	}
340}