// This file is part of Substrate.
// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
use crate::{arg_enums::SyncMode, params::node_key_params::NodeKeyParams};
use clap::Args;
use sc_network::{
config::{
NetworkConfiguration, NodeKeyConfig, NonReservedPeerMode, SetConfig, TransportConfig,
},
multiaddr::Protocol,
};
use sc_service::{
config::{Multiaddr, MultiaddrWithPeerId},
ChainSpec, ChainType,
};
use std::{borrow::Cow, path::PathBuf};
/// Parameters used to create the network configuration.
#[derive(Debug, Clone, Args)]
pub struct NetworkParams {
/// Specify a list of bootnodes.
#[clap(long, value_name = "ADDR", multiple_values(true))]
pub bootnodes: Vec,
/// Specify a list of reserved node addresses.
#[clap(long, value_name = "ADDR", multiple_values(true))]
pub reserved_nodes: Vec,
/// Whether to only synchronize the chain with reserved nodes.
///
/// Also disables automatic peer discovery.
///
/// TCP connections might still be established with non-reserved nodes.
/// In particular, if you are a validator your node might still connect to other
/// validator nodes and collator nodes regardless of whether they are defined as
/// reserved nodes.
#[clap(long)]
pub reserved_only: bool,
/// The public address that other nodes will use to connect to it.
/// This can be used if there's a proxy in front of this node.
#[clap(long, value_name = "PUBLIC_ADDR", multiple_values(true))]
pub public_addr: Vec,
/// Listen on this multiaddress.
///
/// By default:
/// If `--validator` is passed: `/ip4/0.0.0.0/tcp/` and `/ip6/[::]/tcp/`.
/// Otherwise: `/ip4/0.0.0.0/tcp//ws` and `/ip6/[::]/tcp//ws`.
#[clap(long, value_name = "LISTEN_ADDR", multiple_values(true))]
pub listen_addr: Vec,
/// Specify p2p protocol TCP port.
#[clap(long, value_name = "PORT", conflicts_with_all = &[ "listen-addr" ])]
pub port: Option,
/// Always forbid connecting to private IPv4 addresses (as specified in
/// [RFC1918](https://tools.ietf.org/html/rfc1918)), unless the address was passed with
/// `--reserved-nodes` or `--bootnodes`. Enabled by default for chains marked as "live" in
/// their chain specifications.
#[clap(long, conflicts_with_all = &["allow-private-ipv4"])]
pub no_private_ipv4: bool,
/// Always accept connecting to private IPv4 addresses (as specified in
/// [RFC1918](https://tools.ietf.org/html/rfc1918)). Enabled by default for chains marked as
/// "local" in their chain specifications, or when `--dev` is passed.
#[clap(long, conflicts_with_all = &["no-private-ipv4"])]
pub allow_private_ipv4: bool,
/// Specify the number of outgoing connections we're trying to maintain.
#[clap(long, value_name = "COUNT", default_value = "25")]
pub out_peers: u32,
/// Maximum number of inbound full nodes peers.
#[clap(long, value_name = "COUNT", default_value = "25")]
pub in_peers: u32,
/// Maximum number of inbound light nodes peers.
#[clap(long, value_name = "COUNT", default_value = "100")]
pub in_peers_light: u32,
/// Disable mDNS discovery.
///
/// By default, the network will use mDNS to discover other nodes on the
/// local network. This disables it. Automatically implied when using --dev.
#[clap(long)]
pub no_mdns: bool,
/// Maximum number of peers from which to ask for the same blocks in parallel.
///
/// This allows downloading announced blocks from multiple peers. Decrease to save
/// traffic and risk increased latency.
#[clap(long, value_name = "COUNT", default_value = "5")]
pub max_parallel_downloads: u32,
#[allow(missing_docs)]
#[clap(flatten)]
pub node_key_params: NodeKeyParams,
/// Enable peer discovery on local networks.
///
/// By default this option is `true` for `--dev` or when the chain type is
/// `Local`/`Development` and false otherwise.
#[clap(long)]
pub discover_local: bool,
/// Require iterative Kademlia DHT queries to use disjoint paths for increased resiliency in
/// the presence of potentially adversarial nodes.
///
/// See the S/Kademlia paper for more information on the high level design as well as its
/// security improvements.
#[clap(long)]
pub kademlia_disjoint_query_paths: bool,
/// Join the IPFS network and serve transactions over bitswap protocol.
#[clap(long)]
pub ipfs_server: bool,
/// Blockchain syncing mode.
///
/// - `full`: Download and validate full blockchain history.
/// - `fast`: Download blocks and the latest state only.
/// - `fast-unsafe`: Same as `fast`, but skip downloading state proofs.
/// - `warp`: Download the latest state and proof.
#[clap(
long,
arg_enum,
value_name = "SYNC_MODE",
default_value = "full",
ignore_case = true,
verbatim_doc_comment
)]
pub sync: SyncMode,
}
impl NetworkParams {
/// Fill the given `NetworkConfiguration` by looking at the cli parameters.
pub fn network_config(
&self,
chain_spec: &Box,
is_dev: bool,
is_validator: bool,
net_config_path: Option,
client_id: &str,
node_name: &str,
node_key: NodeKeyConfig,
default_listen_port: u16,
) -> NetworkConfiguration {
let port = self.port.unwrap_or(default_listen_port);
let listen_addresses = if self.listen_addr.is_empty() {
if is_validator || is_dev {
vec![
Multiaddr::empty()
.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
.with(Protocol::Tcp(port)),
Multiaddr::empty()
.with(Protocol::Ip4([0, 0, 0, 0].into()))
.with(Protocol::Tcp(port)),
]
} else {
vec![
Multiaddr::empty()
.with(Protocol::Ip6([0, 0, 0, 0, 0, 0, 0, 0].into()))
.with(Protocol::Tcp(port))
.with(Protocol::Ws(Cow::Borrowed("/"))),
Multiaddr::empty()
.with(Protocol::Ip4([0, 0, 0, 0].into()))
.with(Protocol::Tcp(port))
.with(Protocol::Ws(Cow::Borrowed("/"))),
]
}
} else {
self.listen_addr.clone()
};
let public_addresses = self.public_addr.clone();
let mut boot_nodes = chain_spec.boot_nodes().to_vec();
boot_nodes.extend(self.bootnodes.clone());
let chain_type = chain_spec.chain_type();
// Activate if the user explicitly requested local discovery, `--dev` is given or the
// chain type is `Local`/`Development`
let allow_non_globals_in_dht =
self.discover_local ||
is_dev || matches!(chain_type, ChainType::Local | ChainType::Development);
let allow_private_ipv4 = match (self.allow_private_ipv4, self.no_private_ipv4) {
(true, true) => unreachable!("`*_private_ipv4` flags are mutually exclusive; qed"),
(true, false) => true,
(false, true) => false,
(false, false) =>
is_dev || matches!(chain_type, ChainType::Local | ChainType::Development),
};
NetworkConfiguration {
boot_nodes,
net_config_path,
default_peers_set: SetConfig {
in_peers: self.in_peers + self.in_peers_light,
out_peers: self.out_peers,
reserved_nodes: self.reserved_nodes.clone(),
non_reserved_mode: if self.reserved_only {
NonReservedPeerMode::Deny
} else {
NonReservedPeerMode::Accept
},
},
default_peers_set_num_full: self.in_peers + self.out_peers,
listen_addresses,
public_addresses,
extra_sets: Vec::new(),
request_response_protocols: Vec::new(),
node_key,
node_name: node_name.to_string(),
client_version: client_id.to_string(),
transport: TransportConfig::Normal {
enable_mdns: !is_dev && !self.no_mdns,
allow_private_ipv4,
},
max_parallel_downloads: self.max_parallel_downloads,
enable_dht_random_walk: !self.reserved_only,
allow_non_globals_in_dht,
kademlia_disjoint_query_paths: self.kademlia_disjoint_query_paths,
yamux_window_size: None,
ipfs_server: self.ipfs_server,
sync_mode: self.sync.into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[derive(Parser)]
struct Cli {
#[clap(flatten)]
network_params: NetworkParams,
}
#[test]
fn reserved_nodes_multiple_values_and_occurrences() {
let params = Cli::try_parse_from([
"",
"--reserved-nodes",
"/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
"/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
"--reserved-nodes",
"/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS",
])
.expect("Parses network params");
let expected = vec![
MultiaddrWithPeerId::try_from(
"/ip4/0.0.0.0/tcp/501/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
.to_string(),
)
.unwrap(),
MultiaddrWithPeerId::try_from(
"/ip4/0.0.0.0/tcp/502/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
.to_string(),
)
.unwrap(),
MultiaddrWithPeerId::try_from(
"/ip4/0.0.0.0/tcp/503/p2p/12D3KooWEBo1HUPQJwiBmM5kSeg4XgiVxEArArQdDarYEsGxMfbS"
.to_string(),
)
.unwrap(),
];
assert_eq!(expected, params.network_params.reserved_nodes);
}
#[test]
fn sync_ingores_case() {
let params = Cli::try_parse_from(["", "--sync", "wArP"]).expect("Parses network params");
assert_eq!(SyncMode::Warp, params.network_params.sync);
}
}