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,
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/// 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	#[arg(long, value_name = "PUBLIC_ADDR", num_args = 1..)]
62	pub public_addr: Vec<Multiaddr>,
63
64	/// Listen on this multiaddress.
65	///
66	/// By default:
67	/// If `--validator` is passed: `/ip4/0.0.0.0/tcp/<port>` and `/ip6/[::]/tcp/<port>`.
68	/// Otherwise: `/ip4/0.0.0.0/tcp/<port>/ws` and `/ip6/[::]/tcp/<port>/ws`.
69	///
70	/// Experimental: `/ip4/<ip>/udp/<port>/webrtc-direct` and
71	/// `/ip6/<ip>/udp/<port>/webrtc-direct`. Unspecified addresses
72	/// (`0.0.0.0` / `[::]`) are not supported, bind to a specific IP is required.
73	/// Only works on the litep2p network backend.
74	#[arg(long, value_name = "LISTEN_ADDR", num_args = 1..)]
75	pub listen_addr: Vec<Multiaddr>,
76
77	/// Allow WebRTC addresses. This is an experimental feature.
78	///
79	/// Without this enabled, WebRTC addresses specified in `listen_addr`
80	/// will be skipped. Only works on litep2p network backend.
81	#[arg(long)]
82	pub experimental_webrtc: bool,
83
84	/// Specify p2p protocol TCP port.
85	#[arg(long, value_name = "PORT", conflicts_with_all = &[ "listen_addr" ])]
86	pub port: Option<u16>,
87
88	/// Always forbid connecting to private IPv4/IPv6 addresses.
89	///
90	/// The option doesn't apply to addresses passed with `--reserved-nodes` or
91	/// `--bootnodes`. Enabled by default for chains marked as "live" in their chain
92	/// specifications.
93	///
94	/// Address allocation for private networks is specified by
95	/// [RFC1918](https://tools.ietf.org/html/rfc1918)).
96	#[arg(long, alias = "no-private-ipv4", conflicts_with_all = &["allow_private_ip"])]
97	pub no_private_ip: bool,
98
99	/// Always accept connecting to private IPv4/IPv6 addresses.
100	///
101	/// Enabled by default for chains marked as "local" in their chain specifications,
102	/// or when `--dev` is passed.
103	///
104	/// Address allocation for private networks is specified by
105	/// [RFC1918](https://tools.ietf.org/html/rfc1918)).
106	#[arg(long, alias = "allow-private-ipv4", conflicts_with_all = &["no_private_ip"])]
107	pub allow_private_ip: bool,
108
109	/// Number of outgoing connections we're trying to maintain.
110	#[arg(long, value_name = "COUNT", default_value_t = 8)]
111	pub out_peers: u32,
112
113	/// Maximum number of inbound full nodes peers.
114	#[arg(long, value_name = "COUNT", default_value_t = 32)]
115	pub in_peers: u32,
116
117	/// Maximum number of inbound light nodes peers.
118	#[arg(long, value_name = "COUNT", default_value_t = 100)]
119	pub in_peers_light: u32,
120
121	/// Disable mDNS discovery (default: true).
122	///
123	/// By default, the network will use mDNS to discover other nodes on the
124	/// local network. This disables it. Automatically implied when using --dev.
125	#[arg(long)]
126	pub no_mdns: bool,
127
128	/// Maximum number of peers from which to ask for the same blocks in parallel.
129	///
130	/// This allows downloading announced blocks from multiple peers.
131	/// Decrease to save traffic and risk increased latency.
132	#[arg(long, value_name = "COUNT", default_value_t = 5)]
133	pub max_parallel_downloads: u32,
134
135	#[allow(missing_docs)]
136	#[clap(flatten)]
137	pub node_key_params: NodeKeyParams,
138
139	/// Enable peer discovery on local networks.
140	///
141	/// By default this option is `true` for `--dev` or when the chain type is
142	/// `Local`/`Development` and false otherwise.
143	#[arg(long)]
144	pub discover_local: bool,
145
146	/// Require iterative Kademlia DHT queries to use disjoint paths.
147	///
148	/// Disjoint paths increase resiliency in the presence of potentially adversarial nodes.
149	///
150	/// See the S/Kademlia paper for more information on the high level design as well as its
151	/// security improvements.
152	#[arg(long)]
153	pub kademlia_disjoint_query_paths: bool,
154
155	/// Kademlia replication factor.
156	///
157	/// Determines to how many closest peers a record is replicated to.
158	///
159	/// Discovery mechanism requires successful replication to all
160	/// `kademlia_replication_factor` peers to consider record successfully put.
161	#[arg(long, default_value = "20")]
162	pub kademlia_replication_factor: NonZeroUsize,
163
164	/// Join the IPFS network and serve transactions over bitswap protocol.
165	#[arg(long)]
166	pub ipfs_server: bool,
167
168	/// Specify a list of IPFS bootstrap nodes.
169	#[arg(long, value_name = "ADDR", num_args = 1.., requires = "ipfs_server")]
170	pub ipfs_bootnodes: Vec<MultiaddrWithPeerId>,
171
172	/// Blockchain syncing mode.
173	#[arg(
174		long,
175		value_enum,
176		value_name = "SYNC_MODE",
177		default_value_t = SyncMode::Full,
178		ignore_case = true,
179		verbatim_doc_comment
180	)]
181	pub sync: SyncMode,
182
183	/// Maximum number of blocks per request.
184	///
185	/// Try reducing this number from the default value if you have a slow network connection
186	/// and observe block requests timing out.
187	#[arg(long, value_name = "COUNT", default_value_t = 64)]
188	pub max_blocks_per_request: u32,
189
190	/// Network backend used for P2P networking.
191	///
192	/// Litep2p is a lightweight alternative to libp2p, that is designed to be more
193	/// efficient and easier to use. At the same time, litep2p brings performance
194	/// improvements and reduces the CPU usage significantly.
195	///
196	/// Libp2p is the old network backend, that may still be used for compatibility
197	/// reasons until the whole ecosystem is migrated to litep2p.
198	#[arg(
199		long,
200		value_enum,
201		value_name = "NETWORK_BACKEND",
202		default_value_t = NetworkBackendType::Litep2p,
203		ignore_case = true,
204		verbatim_doc_comment
205	)]
206	pub network_backend: NetworkBackendType,
207}
208
209impl NetworkParams {
210	/// Fill the given `NetworkConfiguration` by looking at the cli parameters.
211	pub fn network_config(
212		&self,
213		chain_spec: &Box<dyn ChainSpec>,
214		is_dev: bool,
215		is_validator: bool,
216		net_config_path: Option<PathBuf>,
217		client_id: &str,
218		node_name: &str,
219		node_key: NodeKeyConfig,
220		default_listen_port: u16,
221	) -> NetworkConfiguration {
222		let port = self.port.unwrap_or(default_listen_port);
223
224		let listen_addresses = if self.listen_addr.is_empty() {
225			if is_validator || is_dev {
226				vec![
227					Multiaddr::empty()
228						.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
229						.with(Protocol::Tcp(port)),
230					Multiaddr::empty()
231						.with(Protocol::Ip4([0, 0, 0, 0].into()))
232						.with(Protocol::Tcp(port)),
233				]
234			} else {
235				vec![
236					Multiaddr::empty()
237						.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
238						.with(Protocol::Tcp(port))
239						.with(Protocol::Ws(Cow::Borrowed("/"))),
240					Multiaddr::empty()
241						.with(Protocol::Ip4([0, 0, 0, 0].into()))
242						.with(Protocol::Tcp(port))
243						.with(Protocol::Ws(Cow::Borrowed("/"))),
244				]
245			}
246		} else {
247			self.listen_addr.clone()
248		};
249
250		let public_addresses = self.public_addr.clone();
251
252		let mut boot_nodes = chain_spec.boot_nodes().to_vec();
253		boot_nodes.extend(self.bootnodes.clone());
254
255		let chain_type = chain_spec.chain_type();
256		// Activate if the user explicitly requested local discovery, `--dev` is given or the
257		// chain type is `Local`/`Development`
258		let allow_non_globals_in_dht =
259			self.discover_local ||
260				is_dev || matches!(chain_type, ChainType::Local | ChainType::Development);
261
262		let allow_private_ip = match (self.allow_private_ip, self.no_private_ip) {
263			(true, true) => unreachable!("`*_private_ip` flags are mutually exclusive; qed"),
264			(true, false) => true,
265			(false, true) => false,
266			(false, false) => {
267				is_dev || matches!(chain_type, ChainType::Local | ChainType::Development)
268			},
269		};
270
271		NetworkConfiguration {
272			boot_nodes,
273			net_config_path,
274			default_peers_set: SetConfig {
275				in_peers: self.in_peers + self.in_peers_light,
276				out_peers: self.out_peers,
277				reserved_nodes: self.reserved_nodes.clone(),
278				non_reserved_mode: if self.reserved_only {
279					NonReservedPeerMode::Deny
280				} else {
281					NonReservedPeerMode::Accept
282				},
283			},
284			default_peers_set_num_full: self.in_peers + self.out_peers,
285			listen_addresses,
286			experimental_webrtc: self.experimental_webrtc,
287			public_addresses,
288			node_key,
289			node_name: node_name.to_string(),
290			client_version: client_id.to_string(),
291			transport: TransportConfig::Normal {
292				enable_mdns: !is_dev && !self.no_mdns,
293				allow_private_ip,
294			},
295			idle_connection_timeout: DEFAULT_IDLE_CONNECTION_TIMEOUT,
296			max_parallel_downloads: self.max_parallel_downloads,
297			max_blocks_per_request: self.max_blocks_per_request,
298			min_peers_to_start_warp_sync: None,
299			enable_dht_random_walk: !self.reserved_only,
300			allow_non_globals_in_dht,
301			kademlia_disjoint_query_paths: self.kademlia_disjoint_query_paths,
302			kademlia_replication_factor: self.kademlia_replication_factor,
303			ipfs_server: self.ipfs_server,
304			ipfs_bootnodes: self.ipfs_bootnodes.clone(),
305			sync_mode: self.sync.into(),
306			network_backend: self.network_backend.into(),
307		}
308	}
309}
310
311#[cfg(test)]
312mod tests {
313	use super::*;
314	use clap::Parser;
315
316	#[derive(Parser)]
317	struct Cli {
318		#[clap(flatten)]
319		network_params: NetworkParams,
320	}
321
322	#[test]
323	fn reserved_nodes_multiple_values_and_occurrences() {
324		let params = Cli::try_parse_from([
325			"",
326			"--reserved-nodes",
327			"/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
328			"/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
329			"--reserved-nodes",
330			"/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
331		])
332		.expect("Parses network params");
333
334		let expected = vec![
335			MultiaddrWithPeerId::try_from(
336				"/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
337					.to_string(),
338			)
339			.unwrap(),
340			MultiaddrWithPeerId::try_from(
341				"/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
342					.to_string(),
343			)
344			.unwrap(),
345			MultiaddrWithPeerId::try_from(
346				"/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
347					.to_string(),
348			)
349			.unwrap(),
350		];
351
352		assert_eq!(expected, params.network_params.reserved_nodes);
353	}
354
355	#[test]
356	fn sync_ignores_case() {
357		let params = Cli::try_parse_from(["", "--sync", "wArP"]).expect("Parses network params");
358
359		assert_eq!(SyncMode::Warp, params.network_params.sync);
360	}
361}