referrerpolicy=no-referrer-when-downgrade

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		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/// Parameters used to create the network configuration.
39#[derive(Debug, Clone, Args)]
40pub struct NetworkParams {
41	/// Specify a list of bootnodes.
42	#[arg(long, value_name = "ADDR", num_args = 1..)]
43	pub bootnodes: Vec<MultiaddrWithPeerId>,
44
45	/// Specify a list of reserved node addresses.
46	#[arg(long, value_name = "ADDR", num_args = 1..)]
47	pub reserved_nodes: Vec<MultiaddrWithPeerId>,
48
49	/// Whether to only synchronize the chain with reserved nodes.
50	///
51	/// Also disables automatic peer discovery.
52	/// TCP connections might still be established with non-reserved nodes.
53	/// In particular, if you are a validator your node might still connect to other
54	/// validator nodes and collator nodes regardless of whether they are defined as
55	/// reserved nodes.
56	#[arg(long)]
57	pub reserved_only: bool,
58
59	/// Public address that other nodes will use to connect to this node.
60	///
61	/// This can be used if there's a proxy in front of this node.
62	///
63	/// A `webrtc-direct` address must be given as
64	/// `/<ip4|ip6|dns|dns4|dns6>/<host>/udp/<port>/webrtc-direct`. The node appends its own
65	/// `/certhash/...` and `/p2p/...`.
66	///
67	/// A `/p2p/<peer id>` and, on a `webrtc-direct` address, a `/certhash/<hash>` may be given.
68	/// Both are checked against the node key and removed, and the node doesn't start if either
69	/// disagrees with it.
70	#[arg(long, value_name = "PUBLIC_ADDR", num_args = 1..)]
71	pub public_addr: Vec<Multiaddr>,
72
73	/// Listen on this multiaddress.
74	///
75	/// By default:
76	/// If `--validator` is passed: `/ip4/0.0.0.0/tcp/<port>` and `/ip6/[::]/tcp/<port>`.
77	/// Otherwise: `/ip4/0.0.0.0/tcp/<port>/ws` and `/ip6/[::]/tcp/<port>/ws`, plus, on the
78	/// litep2p network backend, `/ip4/0.0.0.0/udp/<port>/webrtc-direct` and
79	/// `/ip6/[::]/udp/<port>/webrtc-direct`.
80	///
81	/// WebRTC addresses (`/ip4/<ip>/udp/<port>/webrtc-direct` and
82	/// `/ip6/<ip>/udp/<port>/webrtc-direct`) only work on the litep2p network backend.
83	///
84	/// A `/p2p/<peer id>` and, on a `webrtc-direct` address, a `/certhash/<hash>` may be given.
85	/// Both are checked against the node key and removed, and the node doesn't start if either
86	/// disagrees with it.
87	#[arg(long, value_name = "LISTEN_ADDR", num_args = 1..)]
88	pub listen_addr: Vec<Multiaddr>,
89
90	/// Listen on WebRTC addresses even on a validator or collator (on para- &
91	/// relaychain side of the node depending on the position of the flag).
92	///
93	/// Only applies if no explicit `--listen-addr` is passed. Only works on the
94	/// litep2p network backend.
95	#[arg(long)]
96	pub force_enable_webrtc: bool,
97
98	/// Specify p2p protocol TCP port.
99	#[arg(long, value_name = "PORT", conflicts_with_all = &[ "listen_addr" ])]
100	pub port: Option<u16>,
101
102	/// Always forbid connecting to private IPv4/IPv6 addresses.
103	///
104	/// The option doesn't apply to addresses passed with `--reserved-nodes` or
105	/// `--bootnodes`. Enabled by default for chains marked as "live" in their chain
106	/// specifications.
107	///
108	/// Address allocation for private networks is specified by
109	/// [RFC1918](https://tools.ietf.org/html/rfc1918)).
110	#[arg(long, alias = "no-private-ipv4", conflicts_with_all = &["allow_private_ip"])]
111	pub no_private_ip: bool,
112
113	/// Always accept connecting to private IPv4/IPv6 addresses.
114	///
115	/// Enabled by default for chains marked as "local" in their chain specifications,
116	/// or when `--dev` is passed.
117	///
118	/// Address allocation for private networks is specified by
119	/// [RFC1918](https://tools.ietf.org/html/rfc1918)).
120	#[arg(long, alias = "allow-private-ipv4", conflicts_with_all = &["no_private_ip"])]
121	pub allow_private_ip: bool,
122
123	/// Number of outgoing connections we're trying to maintain.
124	#[arg(long, value_name = "COUNT", default_value_t = 8)]
125	pub out_peers: u32,
126
127	/// Maximum number of inbound full nodes peers.
128	#[arg(long, value_name = "COUNT", default_value_t = 32)]
129	pub in_peers: u32,
130
131	/// Maximum number of inbound light nodes peers.
132	#[arg(long, value_name = "COUNT", default_value_t = 500)]
133	pub in_peers_light: u32,
134
135	/// Disable mDNS discovery (default: true).
136	///
137	/// By default, the network will use mDNS to discover other nodes on the
138	/// local network. This disables it. Automatically implied when using --dev.
139	#[arg(long)]
140	pub no_mdns: bool,
141
142	/// Maximum number of peers from which to ask for the same blocks in parallel.
143	///
144	/// This allows downloading announced blocks from multiple peers.
145	/// Decrease to save traffic and risk increased latency.
146	#[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	/// Enable peer discovery on local networks.
154	///
155	/// By default this option is `true` for `--dev` or when the chain type is
156	/// `Local`/`Development` and false otherwise.
157	#[arg(long)]
158	pub discover_local: bool,
159
160	/// Require iterative Kademlia DHT queries to use disjoint paths.
161	///
162	/// Disjoint paths increase resiliency in the presence of potentially adversarial nodes.
163	///
164	/// See the S/Kademlia paper for more information on the high level design as well as its
165	/// security improvements.
166	#[arg(long)]
167	pub kademlia_disjoint_query_paths: bool,
168
169	/// Kademlia replication factor.
170	///
171	/// Determines to how many closest peers a record is replicated to.
172	///
173	/// Discovery mechanism requires successful replication to all
174	/// `kademlia_replication_factor` peers to consider record successfully put.
175	#[arg(long, default_value = "20")]
176	pub kademlia_replication_factor: NonZeroUsize,
177
178	/// Join the IPFS network and serve transactions over bitswap protocol.
179	#[arg(long)]
180	pub ipfs_server: bool,
181
182	/// Specify a list of IPFS bootstrap nodes.
183	#[arg(long, value_name = "ADDR", num_args = 1.., requires = "ipfs_server")]
184	pub ipfs_bootnodes: Vec<MultiaddrWithPeerId>,
185
186	/// Wall-clock limit, in milliseconds, for a single runtime call serving an (untrusted)
187	/// light-client request.
188	///
189	/// `0` disables the limit.
190	///
191	/// The limit is enforced in 100ms steps, so a call may overrun it by up to 200ms.
192	///
193	/// Requests exceeding the limit are interrupted and answered with an empty proof.
194	#[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	/// Number of light-client call requests executed in parallel.
202	///
203	/// Defaults to half the available CPU cores, at most 4. Every worker may use ~128 MiB of
204	/// memory.
205	#[arg(long, value_name = "COUNT")]
206	pub light_request_call_workers: Option<NonZeroUsize>,
207
208	/// Blockchain syncing mode.
209	#[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	/// Maximum number of blocks per request.
220	///
221	/// Try reducing this number from the default value if you have a slow network connection
222	/// and observe block requests timing out.
223	#[arg(long, value_name = "COUNT", default_value_t = 64)]
224	pub max_blocks_per_request: u32,
225
226	/// Network backend used for P2P networking.
227	///
228	/// Litep2p is a lightweight alternative to libp2p, that is designed to be more
229	/// efficient and easier to use. At the same time, litep2p brings performance
230	/// improvements and reduces the CPU usage significantly.
231	///
232	/// Libp2p is the old network backend, that may still be used for compatibility
233	/// reasons until the whole ecosystem is migrated to litep2p.
234	#[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	/// Fill the given `NetworkConfiguration` by looking at the cli parameters.
247	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		// Activate if the user explicitly requested local discovery, `--dev` is given or the
318		// chain type is `Local`/`Development`
319		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}