Files
lighthouse/boot_node/src/server.rs
Daniel Knopik ee1b6bc81b Create network_utils crate (#7761)
Anchor currently depends on `lighthouse_network` for a few types and utilities that live within. As we use our own libp2p behaviours, we actually do not use the core logic in that crate. This makes us transitively depend on a bunch of unneeded crates (even a whole separate libp2p if the versions mismatch!)


  Move things we require into it's own lightweight crate.


Co-Authored-By: Daniel Knopik <daniel@dknopik.de>
2025-09-10 12:59:24 +00:00

159 lines
5.6 KiB
Rust

//! The main bootnode server execution.
use super::BootNodeConfig;
use crate::config::BootNodeConfigSerialization;
use clap::ArgMatches;
use eth2_network_config::Eth2NetworkConfig;
use lighthouse_network::{
Eth2Enr,
discv5::{self, Discv5, enr::NodeId},
};
use network_utils::enr_ext::EnrExt;
use tracing::{info, warn};
use types::EthSpec;
pub async fn run<E: EthSpec>(
lh_matches: &ArgMatches,
bn_matches: &ArgMatches,
eth2_network_config: &Eth2NetworkConfig,
) -> Result<(), String> {
// parse the CLI args into a useable config
let config: BootNodeConfig<E> = BootNodeConfig::new(bn_matches, eth2_network_config).await?;
// Dump configs if `dump-config` or `dump-chain-config` flags are set
let config_sz = BootNodeConfigSerialization::from_config_ref(&config);
clap_utils::check_dump_configs::<_, E>(
lh_matches,
&config_sz,
&eth2_network_config.chain_spec::<E>()?,
)?;
if lh_matches.get_flag("immediate-shutdown") {
return Ok(());
}
let BootNodeConfig {
boot_nodes,
local_enr,
local_key,
discv5_config,
..
} = config;
// Print out useful information about the generated ENR
let enr_v4_socket = local_enr.udp4_socket();
let enr_v6_socket = local_enr.udp6_socket();
let eth2_field = local_enr
.eth2()
.map(|fork_id| hex::encode(fork_id.fork_digest))
.unwrap_or_default();
let pretty_v4_socket = enr_v4_socket.as_ref().map(|addr| addr.to_string());
let pretty_v6_socket = enr_v6_socket.as_ref().map(|addr| addr.to_string());
info!(
listening_address = ?discv5_config.listen_config,
advertised_v4_address = ?pretty_v4_socket,
advertised_v6_address = ?pretty_v6_socket,
eth2 = eth2_field,
"Configuration parameters"
);
info!(peer_id = %local_enr.peer_id(), node_id = %local_enr.node_id(), "Identity established");
// build the contactable multiaddr list, adding the p2p protocol
info!(enr = local_enr.to_base64(), "Contact information");
info!(enr = ?local_enr, "Enr details");
info!(multiaddrs = ?local_enr.multiaddr_p2p(), "Contact information");
// construct the discv5 server
let mut discv5: Discv5 = Discv5::new(local_enr.clone(), local_key, discv5_config).unwrap();
// If there are any bootnodes add them to the routing table
for enr in boot_nodes {
info!(
ipv4_address = ?enr.udp4_socket(),
ipv6_address = ?enr.udp6_socket(),
peer_id = ?enr.peer_id(),
node_id = ?enr.node_id(),
"Adding bootnode"
);
if enr != local_enr
&& let Err(e) = discv5.add_enr(enr)
{
warn!(error = ?e, "Failed adding ENR");
}
}
// start the server
if let Err(e) = discv5.start().await {
return Err(format!("Could not start discv5 server: {e:?}"));
}
// if there are peers in the local routing table, establish a session by running a query
if !discv5.table_entries_id().is_empty() {
info!("Executing bootstrap query...");
let _ = discv5.find_node(NodeId::random()).await;
}
// respond with metrics every 10 seconds
let mut metric_interval = tokio::time::interval(tokio::time::Duration::from_secs(10));
// get an event stream
let mut event_stream = match discv5.event_stream().await {
Ok(stream) => stream,
Err(e) => {
return Err(format!("Failed to obtain event stream: {e:?}"));
}
};
// listen for events
loop {
tokio::select! {
_ = metric_interval.tick() => {
// Get some ipv4/ipv6 stats to add in the metrics.
let mut ipv4_only_reachable: usize = 0;
let mut ipv6_only_reachable: usize= 0;
let mut ipv4_ipv6_reachable: usize = 0;
let mut unreachable_nodes: usize = 0;
for enr in discv5.kbuckets().iter_ref().filter_map(|entry| entry.status.is_connected().then_some(entry.node.value)) {
let declares_ipv4 = enr.udp4_socket().is_some();
let declares_ipv6 = enr.udp6_socket().is_some();
match (declares_ipv4, declares_ipv6) {
(true, true) => ipv4_ipv6_reachable += 1,
(true, false) => ipv4_only_reachable += 1,
(false, true) => ipv6_only_reachable += 1,
(false, false) => unreachable_nodes += 1,
}
}
// display server metrics
let metrics = discv5.metrics();
info!(
connected_peers = discv5.connected_peers(),
active_sessions = metrics.active_sessions,
"requests/s" = format_args!("{:.2}", metrics.unsolicited_requests_per_second),
ipv4_nodes = ipv4_only_reachable,
ipv6_only_nodes = ipv6_only_reachable,
dual_stack_nodes = ipv4_ipv6_reachable,
unreachable_nodes,
"Server metrics",
);
}
Some(event) = event_stream.recv() => {
match event {
discv5::Event::Discovered(_enr) => {
// An ENR has been obtained by the server
// Ignore these events here
}
discv5::Event::SocketUpdated(socket_addr) => {
info!(%socket_addr, "Advertised socket address updated");
}
_ => {} // Ignore
}
}
}
}
}