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		NetworkConfiguration, NodeKeyConfig, NonReservedPeerMode, SetConfig, TransportConfig,
27		DEFAULT_IDLE_CONNECTION_TIMEOUT, DEFAULT_LIGHT_REQUEST_EXECUTION_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, time::Duration};
36
37/// Parameters used to create the network configuration.
38#[derive(Debug, Clone, Args)]
39pub struct NetworkParams {
40	/// Specify a list of bootnodes.
41	#[arg(long, value_name = "ADDR", num_args = 1..)]
42	pub bootnodes: Vec<MultiaddrWithPeerId>,
43
44	/// Specify a list of reserved node addresses.
45	#[arg(long, value_name = "ADDR", num_args = 1..)]
46	pub reserved_nodes: Vec<MultiaddrWithPeerId>,
47
48	/// Whether to only synchronize the chain with reserved nodes.
49	///
50	/// Also disables automatic peer discovery.
51	/// TCP connections might still be established with non-reserved nodes.
52	/// In particular, if you are a validator your node might still connect to other
53	/// validator nodes and collator nodes regardless of whether they are defined as
54	/// reserved nodes.
55	#[arg(long)]
56	pub reserved_only: bool,
57
58	/// Public address that other nodes will use to connect to this node.
59	///
60	/// This can be used if there's a proxy in front of this node.
61	///
62	/// A `webrtc-direct` address must be given as
63	/// `/<ip4|ip6|dns|dns4|dns6>/<host>/udp/<port>/webrtc-direct`. The node appends its own
64	/// `/certhash/...` and `/p2p/...`.
65	///
66	/// A `/p2p/<peer id>` and, on a `webrtc-direct` address, a `/certhash/<hash>` may be given.
67	/// Both are checked against the node key and removed, and the node doesn't start if either
68	/// disagrees with it.
69	#[arg(long, value_name = "PUBLIC_ADDR", num_args = 1..)]
70	pub public_addr: Vec<Multiaddr>,
71
72	/// Listen on this multiaddress.
73	///
74	/// By default:
75	/// If `--validator` is passed: `/ip4/0.0.0.0/tcp/<port>` and `/ip6/[::]/tcp/<port>`.
76	/// Otherwise: `/ip4/0.0.0.0/tcp/<port>/ws` and `/ip6/[::]/tcp/<port>/ws`, plus, on the
77	/// litep2p network backend, `/ip4/0.0.0.0/udp/<port>/webrtc-direct` and
78	/// `/ip6/[::]/udp/<port>/webrtc-direct`.
79	///
80	/// WebRTC addresses (`/ip4/<ip>/udp/<port>/webrtc-direct` and
81	/// `/ip6/<ip>/udp/<port>/webrtc-direct`) only work on the litep2p network backend.
82	///
83	/// A `/p2p/<peer id>` and, on a `webrtc-direct` address, a `/certhash/<hash>` may be given.
84	/// Both are checked against the node key and removed, and the node doesn't start if either
85	/// disagrees with it.
86	#[arg(long, value_name = "LISTEN_ADDR", num_args = 1..)]
87	pub listen_addr: Vec<Multiaddr>,
88
89	/// Listen on WebRTC addresses even on a validator or collator (on para- &
90	/// relaychain side of the node depending on the position of the flag).
91	///
92	/// Only applies if no explicit `--listen-addr` is passed. Only works on the
93	/// litep2p network backend.
94	#[arg(long)]
95	pub force_enable_webrtc: bool,
96
97	/// Specify p2p protocol TCP port.
98	#[arg(long, value_name = "PORT", conflicts_with_all = &[ "listen_addr" ])]
99	pub port: Option<u16>,
100
101	/// Always forbid connecting to private IPv4/IPv6 addresses.
102	///
103	/// The option doesn't apply to addresses passed with `--reserved-nodes` or
104	/// `--bootnodes`. Enabled by default for chains marked as "live" in their chain
105	/// specifications.
106	///
107	/// Address allocation for private networks is specified by
108	/// [RFC1918](https://tools.ietf.org/html/rfc1918)).
109	#[arg(long, alias = "no-private-ipv4", conflicts_with_all = &["allow_private_ip"])]
110	pub no_private_ip: bool,
111
112	/// Always accept connecting to private IPv4/IPv6 addresses.
113	///
114	/// Enabled by default for chains marked as "local" in their chain specifications,
115	/// or when `--dev` is passed.
116	///
117	/// Address allocation for private networks is specified by
118	/// [RFC1918](https://tools.ietf.org/html/rfc1918)).
119	#[arg(long, alias = "allow-private-ipv4", conflicts_with_all = &["no_private_ip"])]
120	pub allow_private_ip: bool,
121
122	/// Number of outgoing connections we're trying to maintain.
123	#[arg(long, value_name = "COUNT", default_value_t = 8)]
124	pub out_peers: u32,
125
126	/// Maximum number of inbound full nodes peers.
127	#[arg(long, value_name = "COUNT", default_value_t = 32)]
128	pub in_peers: u32,
129
130	/// Maximum number of inbound light nodes peers.
131	#[arg(long, value_name = "COUNT", default_value_t = 500)]
132	pub in_peers_light: u32,
133
134	/// Disable mDNS discovery (default: true).
135	///
136	/// By default, the network will use mDNS to discover other nodes on the
137	/// local network. This disables it. Automatically implied when using --dev.
138	#[arg(long)]
139	pub no_mdns: bool,
140
141	/// Maximum number of peers from which to ask for the same blocks in parallel.
142	///
143	/// This allows downloading announced blocks from multiple peers.
144	/// Decrease to save traffic and risk increased latency.
145	#[arg(long, value_name = "COUNT", default_value_t = 5)]
146	pub max_parallel_downloads: u32,
147
148	#[allow(missing_docs)]
149	#[clap(flatten)]
150	pub node_key_params: NodeKeyParams,
151
152	/// Enable peer discovery on local networks.
153	///
154	/// By default this option is `true` for `--dev` or when the chain type is
155	/// `Local`/`Development` and false otherwise.
156	#[arg(long)]
157	pub discover_local: bool,
158
159	/// Require iterative Kademlia DHT queries to use disjoint paths.
160	///
161	/// Disjoint paths increase resiliency in the presence of potentially adversarial nodes.
162	///
163	/// See the S/Kademlia paper for more information on the high level design as well as its
164	/// security improvements.
165	#[arg(long)]
166	pub kademlia_disjoint_query_paths: bool,
167
168	/// Kademlia replication factor.
169	///
170	/// Determines to how many closest peers a record is replicated to.
171	///
172	/// Discovery mechanism requires successful replication to all
173	/// `kademlia_replication_factor` peers to consider record successfully put.
174	#[arg(long, default_value = "20")]
175	pub kademlia_replication_factor: NonZeroUsize,
176
177	/// Join the IPFS network and serve transactions over bitswap protocol.
178	#[arg(long)]
179	pub ipfs_server: bool,
180
181	/// Specify a list of IPFS bootstrap nodes.
182	#[arg(long, value_name = "ADDR", num_args = 1.., requires = "ipfs_server")]
183	pub ipfs_bootnodes: Vec<MultiaddrWithPeerId>,
184
185	/// Wall-clock limit, in milliseconds, for a single runtime call serving an (untrusted)
186	/// light-client request.
187	///
188	/// `0` disables the limit.
189	///
190	/// The limit is enforced in 100ms steps, so a call may overrun it by up to 200ms.
191	///
192	/// Requests exceeding the limit are interrupted and answered with an empty proof.
193	#[arg(
194		long,
195		value_name = "MILLISECONDS",
196		default_value_t = DEFAULT_LIGHT_REQUEST_EXECUTION_TIMEOUT.as_millis() as u64
197	)]
198	pub light_request_execution_timeout_ms: u64,
199
200	/// Blockchain syncing mode.
201	#[arg(
202		long,
203		value_enum,
204		value_name = "SYNC_MODE",
205		default_value_t = SyncMode::Full,
206		ignore_case = true,
207		verbatim_doc_comment
208	)]
209	pub sync: SyncMode,
210
211	/// Maximum number of blocks per request.
212	///
213	/// Try reducing this number from the default value if you have a slow network connection
214	/// and observe block requests timing out.
215	#[arg(long, value_name = "COUNT", default_value_t = 64)]
216	pub max_blocks_per_request: u32,
217
218	/// Network backend used for P2P networking.
219	///
220	/// Litep2p is a lightweight alternative to libp2p, that is designed to be more
221	/// efficient and easier to use. At the same time, litep2p brings performance
222	/// improvements and reduces the CPU usage significantly.
223	///
224	/// Libp2p is the old network backend, that may still be used for compatibility
225	/// reasons until the whole ecosystem is migrated to litep2p.
226	#[arg(
227		long,
228		value_enum,
229		value_name = "NETWORK_BACKEND",
230		default_value_t = NetworkBackendType::Litep2p,
231		ignore_case = true,
232		verbatim_doc_comment
233	)]
234	pub network_backend: NetworkBackendType,
235}
236
237impl NetworkParams {
238	/// Fill the given `NetworkConfiguration` by looking at the cli parameters.
239	pub fn network_config(
240		&self,
241		chain_spec: &Box<dyn ChainSpec>,
242		is_dev: bool,
243		is_validator: bool,
244		net_config_path: Option<PathBuf>,
245		client_id: &str,
246		node_name: &str,
247		node_key: NodeKeyConfig,
248		default_listen_port: u16,
249	) -> NetworkConfiguration {
250		let port = self.port.unwrap_or(default_listen_port);
251
252		if self.force_enable_webrtc && !matches!(self.network_backend, NetworkBackendType::Litep2p)
253		{
254			log::warn!(
255				"`--force-enable-webrtc` has no effect: WebRTC is only supported by the litep2p \
256				 network backend",
257			);
258		}
259
260		let listen_addresses = if self.listen_addr.is_empty() {
261			let mut listen_addresses = if is_validator || is_dev {
262				vec![
263					Multiaddr::empty()
264						.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
265						.with(Protocol::Tcp(port)),
266					Multiaddr::empty()
267						.with(Protocol::Ip4([0, 0, 0, 0].into()))
268						.with(Protocol::Tcp(port)),
269				]
270			} else {
271				vec![
272					Multiaddr::empty()
273						.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
274						.with(Protocol::Tcp(port))
275						.with(Protocol::Ws(Cow::Borrowed("/"))),
276					Multiaddr::empty()
277						.with(Protocol::Ip4([0, 0, 0, 0].into()))
278						.with(Protocol::Tcp(port))
279						.with(Protocol::Ws(Cow::Borrowed("/"))),
280				]
281			};
282
283			if matches!(self.network_backend, NetworkBackendType::Litep2p) &&
284				(self.force_enable_webrtc || !is_validator)
285			{
286				listen_addresses.extend([
287					Multiaddr::empty()
288						.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
289						.with(Protocol::Udp(port))
290						.with(Protocol::WebRTCDirect),
291					Multiaddr::empty()
292						.with(Protocol::Ip4([0, 0, 0, 0].into()))
293						.with(Protocol::Udp(port))
294						.with(Protocol::WebRTCDirect),
295				]);
296			}
297
298			listen_addresses
299		} else {
300			self.listen_addr.clone()
301		};
302
303		let public_addresses = self.public_addr.clone();
304
305		let mut boot_nodes = chain_spec.boot_nodes().to_vec();
306		boot_nodes.extend(self.bootnodes.clone());
307
308		let chain_type = chain_spec.chain_type();
309		// Activate if the user explicitly requested local discovery, `--dev` is given or the
310		// chain type is `Local`/`Development`
311		let allow_non_globals_in_dht =
312			self.discover_local ||
313				is_dev || matches!(chain_type, ChainType::Local | ChainType::Development);
314
315		let allow_private_ip = match (self.allow_private_ip, self.no_private_ip) {
316			(true, true) => unreachable!("`*_private_ip` flags are mutually exclusive; qed"),
317			(true, false) => true,
318			(false, true) => false,
319			(false, false) => {
320				is_dev || matches!(chain_type, ChainType::Local | ChainType::Development)
321			},
322		};
323
324		NetworkConfiguration {
325			boot_nodes,
326			net_config_path,
327			default_peers_set: SetConfig {
328				in_peers: self.in_peers + self.in_peers_light,
329				out_peers: self.out_peers,
330				reserved_nodes: self.reserved_nodes.clone(),
331				non_reserved_mode: if self.reserved_only {
332					NonReservedPeerMode::Deny
333				} else {
334					NonReservedPeerMode::Accept
335				},
336			},
337			default_peers_set_num_full: self.in_peers + self.out_peers,
338			listen_addresses,
339			public_addresses,
340			node_key,
341			node_name: node_name.to_string(),
342			client_version: client_id.to_string(),
343			transport: TransportConfig::Normal {
344				enable_mdns: !is_dev && !self.no_mdns,
345				allow_private_ip,
346			},
347			idle_connection_timeout: DEFAULT_IDLE_CONNECTION_TIMEOUT,
348			max_parallel_downloads: self.max_parallel_downloads,
349			max_blocks_per_request: self.max_blocks_per_request,
350			min_peers_to_start_warp_sync: None,
351			enable_dht_random_walk: !self.reserved_only,
352			allow_non_globals_in_dht,
353			kademlia_disjoint_query_paths: self.kademlia_disjoint_query_paths,
354			kademlia_replication_factor: self.kademlia_replication_factor,
355			ipfs_server: self.ipfs_server,
356			ipfs_bootnodes: self.ipfs_bootnodes.clone(),
357			light_request_execution_timeout: match self.light_request_execution_timeout_ms {
358				0 => None,
359				ms => Some(Duration::from_millis(ms)),
360			},
361			sync_mode: self.sync.into(),
362			network_backend: self.network_backend.into(),
363		}
364	}
365}
366
367#[cfg(test)]
368mod tests {
369	use super::*;
370	use clap::Parser;
371
372	#[derive(Parser)]
373	struct Cli {
374		#[clap(flatten)]
375		network_params: NetworkParams,
376	}
377
378	#[test]
379	fn reserved_nodes_multiple_values_and_occurrences() {
380		let params = Cli::try_parse_from([
381			"",
382			"--reserved-nodes",
383			"/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
384			"/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
385			"--reserved-nodes",
386			"/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
387		])
388		.expect("Parses network params");
389
390		let expected = vec![
391			MultiaddrWithPeerId::try_from(
392				"/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
393					.to_string(),
394			)
395			.unwrap(),
396			MultiaddrWithPeerId::try_from(
397				"/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
398					.to_string(),
399			)
400			.unwrap(),
401			MultiaddrWithPeerId::try_from(
402				"/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
403					.to_string(),
404			)
405			.unwrap(),
406		];
407
408		assert_eq!(expected, params.network_params.reserved_nodes);
409	}
410
411	#[test]
412	fn sync_ignores_case() {
413		let params = Cli::try_parse_from(["", "--sync", "wArP"]).expect("Parses network params");
414
415		assert_eq!(SyncMode::Warp, params.network_params.sync);
416	}
417}