1use crate::{
8 arg_enums::{NetworkBackendType, SyncMode},
9 params::node_key_params::NodeKeyParams,
10};
11use clap::Args;
12use soil_network::{
13 config::{
14 NetworkConfiguration, NodeKeyConfig, NonReservedPeerMode, SetConfig, TransportConfig,
15 DEFAULT_IDLE_CONNECTION_TIMEOUT,
16 },
17 multiaddr::Protocol,
18};
19use soil_service::{
20 config::{Multiaddr, MultiaddrWithPeerId},
21 ChainSpec, ChainType,
22};
23use std::{borrow::Cow, num::NonZeroUsize, path::PathBuf};
24
25#[derive(Debug, Clone, Args)]
27pub struct NetworkParams {
28 #[arg(long, value_name = "ADDR", num_args = 1..)]
30 pub bootnodes: Vec<MultiaddrWithPeerId>,
31
32 #[arg(long, value_name = "ADDR", num_args = 1..)]
34 pub reserved_nodes: Vec<MultiaddrWithPeerId>,
35
36 #[arg(long)]
44 pub reserved_only: bool,
45
46 #[arg(long, value_name = "PUBLIC_ADDR", num_args = 1..)]
50 pub public_addr: Vec<Multiaddr>,
51
52 #[arg(long, value_name = "LISTEN_ADDR", num_args = 1..)]
58 pub listen_addr: Vec<Multiaddr>,
59
60 #[arg(long, value_name = "PORT", conflicts_with_all = &[ "listen_addr" ])]
62 pub port: Option<u16>,
63
64 #[arg(long, alias = "no-private-ipv4", conflicts_with_all = &["allow_private_ip"])]
73 pub no_private_ip: bool,
74
75 #[arg(long, alias = "allow-private-ipv4", conflicts_with_all = &["no_private_ip"])]
83 pub allow_private_ip: bool,
84
85 #[arg(long, value_name = "COUNT", default_value_t = 8)]
87 pub out_peers: u32,
88
89 #[arg(long, value_name = "COUNT", default_value_t = 32)]
91 pub in_peers: u32,
92
93 #[arg(long, value_name = "COUNT", default_value_t = 100)]
95 pub in_peers_light: u32,
96
97 #[arg(long)]
102 pub no_mdns: bool,
103
104 #[arg(long, value_name = "COUNT", default_value_t = 5)]
109 pub max_parallel_downloads: u32,
110
111 #[allow(missing_docs)]
112 #[clap(flatten)]
113 pub node_key_params: NodeKeyParams,
114
115 #[arg(long)]
120 pub discover_local: bool,
121
122 #[arg(long)]
129 pub kademlia_disjoint_query_paths: bool,
130
131 #[arg(long, default_value = "20")]
138 pub kademlia_replication_factor: NonZeroUsize,
139
140 #[arg(long)]
142 pub ipfs_server: bool,
143
144 #[arg(
146 long,
147 value_enum,
148 value_name = "SYNC_MODE",
149 default_value_t = SyncMode::Full,
150 ignore_case = true,
151 verbatim_doc_comment
152 )]
153 pub sync: SyncMode,
154
155 #[arg(long, value_name = "COUNT", default_value_t = 64)]
160 pub max_blocks_per_request: u32,
161
162 #[arg(
171 long,
172 value_enum,
173 value_name = "NETWORK_BACKEND",
174 default_value_t = NetworkBackendType::Litep2p,
175 ignore_case = true,
176 verbatim_doc_comment
177 )]
178 pub network_backend: NetworkBackendType,
179}
180
181impl NetworkParams {
182 pub fn network_config(
184 &self,
185 chain_spec: &Box<dyn ChainSpec>,
186 is_dev: bool,
187 is_validator: bool,
188 net_config_path: Option<PathBuf>,
189 client_id: &str,
190 node_name: &str,
191 node_key: NodeKeyConfig,
192 default_listen_port: u16,
193 ) -> NetworkConfiguration {
194 let port = self.port.unwrap_or(default_listen_port);
195
196 let listen_addresses = if self.listen_addr.is_empty() {
197 if is_validator || is_dev {
198 vec![
199 Multiaddr::empty()
200 .with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
201 .with(Protocol::Tcp(port)),
202 Multiaddr::empty()
203 .with(Protocol::Ip4([0, 0, 0, 0].into()))
204 .with(Protocol::Tcp(port)),
205 ]
206 } else {
207 vec![
208 Multiaddr::empty()
209 .with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
210 .with(Protocol::Tcp(port))
211 .with(Protocol::Ws(Cow::Borrowed("/"))),
212 Multiaddr::empty()
213 .with(Protocol::Ip4([0, 0, 0, 0].into()))
214 .with(Protocol::Tcp(port))
215 .with(Protocol::Ws(Cow::Borrowed("/"))),
216 ]
217 }
218 } else {
219 self.listen_addr.clone()
220 };
221
222 let public_addresses = self.public_addr.clone();
223
224 let mut boot_nodes = chain_spec.boot_nodes().to_vec();
225 boot_nodes.extend(self.bootnodes.clone());
226
227 let chain_type = chain_spec.chain_type();
228 let allow_non_globals_in_dht =
231 self.discover_local
232 || is_dev || matches!(chain_type, ChainType::Local | ChainType::Development);
233
234 let allow_private_ip = match (self.allow_private_ip, self.no_private_ip) {
235 (true, true) => unreachable!("`*_private_ip` flags are mutually exclusive; qed"),
236 (true, false) => true,
237 (false, true) => false,
238 (false, false) => {
239 is_dev || matches!(chain_type, ChainType::Local | ChainType::Development)
240 },
241 };
242
243 NetworkConfiguration {
244 boot_nodes,
245 net_config_path,
246 default_peers_set: SetConfig {
247 in_peers: self.in_peers + self.in_peers_light,
248 out_peers: self.out_peers,
249 reserved_nodes: self.reserved_nodes.clone(),
250 non_reserved_mode: if self.reserved_only {
251 NonReservedPeerMode::Deny
252 } else {
253 NonReservedPeerMode::Accept
254 },
255 },
256 default_peers_set_num_full: self.in_peers + self.out_peers,
257 listen_addresses,
258 public_addresses,
259 node_key,
260 node_name: node_name.to_string(),
261 client_version: client_id.to_string(),
262 transport: TransportConfig::Normal {
263 enable_mdns: !is_dev && !self.no_mdns,
264 allow_private_ip,
265 },
266 idle_connection_timeout: DEFAULT_IDLE_CONNECTION_TIMEOUT,
267 max_parallel_downloads: self.max_parallel_downloads,
268 max_blocks_per_request: self.max_blocks_per_request,
269 min_peers_to_start_warp_sync: None,
270 enable_dht_random_walk: !self.reserved_only,
271 allow_non_globals_in_dht,
272 kademlia_disjoint_query_paths: self.kademlia_disjoint_query_paths,
273 kademlia_replication_factor: self.kademlia_replication_factor,
274 ipfs_server: self.ipfs_server,
275 sync_mode: self.sync.into(),
276 network_backend: self.network_backend.into(),
277 }
278 }
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284 use clap::Parser;
285
286 #[derive(Parser)]
287 struct Cli {
288 #[clap(flatten)]
289 network_params: NetworkParams,
290 }
291
292 #[test]
293 fn reserved_nodes_multiple_values_and_occurrences() {
294 let params = Cli::try_parse_from([
295 "",
296 "--reserved-nodes",
297 "/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
298 "/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
299 "--reserved-nodes",
300 "/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
301 ])
302 .expect("Parses network params");
303
304 let expected = vec![
305 MultiaddrWithPeerId::try_from(
306 "/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
307 .to_string(),
308 )
309 .unwrap(),
310 MultiaddrWithPeerId::try_from(
311 "/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
312 .to_string(),
313 )
314 .unwrap(),
315 MultiaddrWithPeerId::try_from(
316 "/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
317 .to_string(),
318 )
319 .unwrap(),
320 ];
321
322 assert_eq!(expected, params.network_params.reserved_nodes);
323 }
324
325 #[test]
326 fn sync_ignores_case() {
327 let params = Cli::try_parse_from(["", "--sync", "wArP"]).expect("Parses network params");
328
329 assert_eq!(SyncMode::Warp, params.network_params.sync);
330 }
331}