## Issue Addressed Add support for ipv6 and dual stack in lighthouse. ## Proposed Changes From an user perspective, now setting an ipv6 address, optionally configuring the ports should feel exactly the same as using an ipv4 address. If listening over both ipv4 and ipv6 then the user needs to: - use the `--listen-address` two times (ipv4 and ipv6 addresses) - `--port6` becomes then required - `--discovery-port6` can now be used to additionally configure the ipv6 udp port ### Rough list of code changes - Discovery: - Table filter and ip mode set to match the listening config. - Ipv6 address, tcp port and udp port set in the ENR builder - Reported addresses now check which tcp port to give to libp2p - LH Network Service: - Can listen over Ipv6, Ipv4, or both. This uses two sockets. Using mapped addresses is disabled from libp2p and it's the most compatible option. - NetworkGlobals: - No longer stores udp port since was not used at all. Instead, stores the Ipv4 and Ipv6 TCP ports. - NetworkConfig: - Update names to make it clear that previous udp and tcp ports in ENR were Ipv4 - Add fields to configure Ipv6 udp and tcp ports in the ENR - Include advertised enr Ipv6 address. - Add type to model Listening address that's either Ipv4, Ipv6 or both. A listening address includes the ip, udp port and tcp port. - UPnP: - Kept only for ipv4 - Cli flags: - `--listen-addresses` now can take up to two values - `--port` will apply to ipv4 or ipv6 if only one listening address is given. If two listening addresses are given it will apply only to Ipv4. - `--port6` New flag required when listening over ipv4 and ipv6 that applies exclusively to Ipv6. - `--discovery-port` will now apply to ipv4 and ipv6 if only one listening address is given. - `--discovery-port6` New flag to configure the individual udp port of ipv6 if listening over both ipv4 and ipv6. - `--enr-udp-port` Updated docs to specify that it only applies to ipv4. This is an old behaviour. - `--enr-udp6-port` Added to configure the enr udp6 field. - `--enr-tcp-port` Updated docs to specify that it only applies to ipv4. This is an old behaviour. - `--enr-tcp6-port` Added to configure the enr tcp6 field. - `--enr-addresses` now can take two values. - `--enr-match` updated behaviour. - Common: - rename `unused_port` functions to specify that they are over ipv4. - add functions to get unused ports over ipv6. - Testing binaries - Updated code to reflect network config changes and unused_port changes. ## Additional Info TODOs: - use two sockets in discovery. I'll get back to this and it's on https://github.com/sigp/discv5/pull/160 - lcli allow listening over two sockets in generate_bootnodes_enr - add at least one smoke flag for ipv6 (I have tested this and works for me) - update the book
203 lines
7.1 KiB
Rust
203 lines
7.1 KiB
Rust
use beacon_node::{get_data_dir, set_network_config};
|
|
use clap::ArgMatches;
|
|
use eth2_network_config::Eth2NetworkConfig;
|
|
use lighthouse_network::discovery::create_enr_builder_from_config;
|
|
use lighthouse_network::discv5::IpMode;
|
|
use lighthouse_network::discv5::{enr::CombinedKey, Discv5Config, Enr};
|
|
use lighthouse_network::{
|
|
discovery::{load_enr_from_disk, use_or_load_enr},
|
|
load_private_key, CombinedKeyExt, NetworkConfig,
|
|
};
|
|
use serde_derive::{Deserialize, Serialize};
|
|
use ssz::Encode;
|
|
use std::net::SocketAddr;
|
|
use std::{marker::PhantomData, path::PathBuf};
|
|
use types::EthSpec;
|
|
|
|
/// A set of configuration parameters for the bootnode, established from CLI arguments.
|
|
pub struct BootNodeConfig<T: EthSpec> {
|
|
pub listen_socket: SocketAddr,
|
|
// TODO: Generalise to multiaddr
|
|
pub boot_nodes: Vec<Enr>,
|
|
pub local_enr: Enr,
|
|
pub local_key: CombinedKey,
|
|
pub discv5_config: Discv5Config,
|
|
phantom: PhantomData<T>,
|
|
}
|
|
|
|
impl<T: EthSpec> BootNodeConfig<T> {
|
|
pub fn new(
|
|
matches: &ArgMatches<'_>,
|
|
eth2_network_config: &Eth2NetworkConfig,
|
|
) -> Result<Self, String> {
|
|
let data_dir = get_data_dir(matches);
|
|
|
|
// Try and obtain bootnodes
|
|
|
|
let boot_nodes = {
|
|
let mut boot_nodes = Vec::new();
|
|
|
|
if let Some(enr) = ð2_network_config.boot_enr {
|
|
boot_nodes.extend_from_slice(enr);
|
|
}
|
|
|
|
if let Some(nodes) = matches.value_of("boot-nodes") {
|
|
boot_nodes.extend_from_slice(
|
|
&nodes
|
|
.split(',')
|
|
.map(|enr| enr.parse().map_err(|_| format!("Invalid ENR: {}", enr)))
|
|
.collect::<Result<Vec<Enr>, _>>()?,
|
|
);
|
|
}
|
|
|
|
boot_nodes
|
|
};
|
|
|
|
let mut network_config = NetworkConfig::default();
|
|
|
|
let logger = slog_scope::logger();
|
|
|
|
set_network_config(&mut network_config, matches, &data_dir, &logger)?;
|
|
|
|
// Set the Enr UDP ports to the listening ports if not present.
|
|
if let Some(listening_addr_v4) = network_config.listen_addrs().v4() {
|
|
network_config.enr_udp4_port = Some(
|
|
network_config
|
|
.enr_udp4_port
|
|
.unwrap_or(listening_addr_v4.udp_port),
|
|
)
|
|
};
|
|
|
|
if let Some(listening_addr_v6) = network_config.listen_addrs().v6() {
|
|
network_config.enr_udp6_port = Some(
|
|
network_config
|
|
.enr_udp6_port
|
|
.unwrap_or(listening_addr_v6.udp_port),
|
|
)
|
|
};
|
|
|
|
// By default this is enabled. If it is not set, revert to false.
|
|
if !matches.is_present("enable-enr-auto-update") {
|
|
network_config.discv5_config.enr_update = false;
|
|
}
|
|
|
|
// the address to listen on
|
|
let listen_socket = match network_config.listen_addrs().clone() {
|
|
lighthouse_network::ListenAddress::V4(v4_addr) => {
|
|
// Set explicitly as ipv4 otherwise
|
|
network_config.discv5_config.ip_mode = IpMode::Ip4;
|
|
v4_addr.udp_socket_addr()
|
|
}
|
|
lighthouse_network::ListenAddress::V6(v6_addr) => {
|
|
// create ipv6 sockets and enable ipv4 mapped addresses.
|
|
network_config.discv5_config.ip_mode = IpMode::Ip6 {
|
|
enable_mapped_addresses: false,
|
|
};
|
|
|
|
v6_addr.udp_socket_addr()
|
|
}
|
|
lighthouse_network::ListenAddress::DualStack(_v4_addr, v6_addr) => {
|
|
// create ipv6 sockets and enable ipv4 mapped addresses.
|
|
network_config.discv5_config.ip_mode = IpMode::Ip6 {
|
|
enable_mapped_addresses: true,
|
|
};
|
|
|
|
v6_addr.udp_socket_addr()
|
|
}
|
|
};
|
|
|
|
let private_key = load_private_key(&network_config, &logger);
|
|
let local_key = CombinedKey::from_libp2p(&private_key)?;
|
|
|
|
let local_enr = if let Some(dir) = matches.value_of("network-dir") {
|
|
let network_dir: PathBuf = dir.into();
|
|
load_enr_from_disk(&network_dir)?
|
|
} else {
|
|
// build the enr_fork_id and add it to the local_enr if it exists
|
|
let enr_fork = {
|
|
let spec = eth2_network_config.chain_spec::<T>()?;
|
|
|
|
if eth2_network_config.beacon_state_is_known() {
|
|
let genesis_state = eth2_network_config.beacon_state::<T>()?;
|
|
|
|
slog::info!(logger, "Genesis state found"; "root" => genesis_state.canonical_root().to_string());
|
|
let enr_fork = spec.enr_fork_id::<T>(
|
|
types::Slot::from(0u64),
|
|
genesis_state.genesis_validators_root(),
|
|
);
|
|
|
|
Some(enr_fork.as_ssz_bytes())
|
|
} else {
|
|
slog::warn!(
|
|
logger,
|
|
"No genesis state provided. No Eth2 field added to the ENR"
|
|
);
|
|
None
|
|
}
|
|
};
|
|
|
|
// Build the local ENR
|
|
|
|
let mut local_enr = {
|
|
let enable_tcp = false;
|
|
let mut builder = create_enr_builder_from_config(&network_config, enable_tcp);
|
|
// If we know of the ENR field, add it to the initial construction
|
|
if let Some(enr_fork_bytes) = enr_fork {
|
|
builder.add_value("eth2", enr_fork_bytes.as_slice());
|
|
}
|
|
builder
|
|
.build(&local_key)
|
|
.map_err(|e| format!("Failed to build ENR: {:?}", e))?
|
|
};
|
|
|
|
use_or_load_enr(&local_key, &mut local_enr, &network_config, &logger)?;
|
|
local_enr
|
|
};
|
|
|
|
Ok(BootNodeConfig {
|
|
listen_socket,
|
|
boot_nodes,
|
|
local_enr,
|
|
local_key,
|
|
discv5_config: network_config.discv5_config,
|
|
phantom: PhantomData,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// The set of configuration parameters that can safely be (de)serialized.
|
|
///
|
|
/// Its fields are a subset of the fields of `BootNodeConfig`, some of them are copied from `Discv5Config`.
|
|
#[derive(Serialize, Deserialize)]
|
|
pub struct BootNodeConfigSerialization {
|
|
pub listen_socket: SocketAddr,
|
|
// TODO: Generalise to multiaddr
|
|
pub boot_nodes: Vec<Enr>,
|
|
pub local_enr: Enr,
|
|
pub disable_packet_filter: bool,
|
|
pub enable_enr_auto_update: bool,
|
|
}
|
|
|
|
impl BootNodeConfigSerialization {
|
|
/// Returns a `BootNodeConfigSerialization` obtained from copying resp. cloning the
|
|
/// relevant fields of `config`
|
|
pub fn from_config_ref<T: EthSpec>(config: &BootNodeConfig<T>) -> Self {
|
|
let BootNodeConfig {
|
|
listen_socket,
|
|
boot_nodes,
|
|
local_enr,
|
|
local_key: _,
|
|
discv5_config,
|
|
phantom: _,
|
|
} = config;
|
|
|
|
BootNodeConfigSerialization {
|
|
listen_socket: *listen_socket,
|
|
boot_nodes: boot_nodes.clone(),
|
|
local_enr: local_enr.clone(),
|
|
disable_packet_filter: !discv5_config.enable_packet_filter,
|
|
enable_enr_auto_update: discv5_config.enr_update,
|
|
}
|
|
}
|
|
}
|