76a2007b64
Currently Lighthouse will remain uncontactable if users port forward a port that is not the same as the one they are listening on. For example, if Lighthouse runs with port 9000 TCP/UDP locally but a router is configured to pass 9010 externally to the lighthouse node on 9000, other nodes on the network will not be able to reach the lighthouse node. This occurs because Lighthouse does not update its ENR TCP port on external socket discovery. The intention was always that users should use `--enr-tcp-port` to customise this, but this is non-intuitive. The difficulty arises because we have no discovery mechanism to find our external TCP port. If we discovery a new external UDP port, we must guess what our external TCP port might be. This PR assumes the external TCP port is the same as the external UDP port (which may not be the case) and thus updates the TCP port along with the UDP port if the `--enr-tcp-port` flag is not set. Along with this PR, will be added documentation to the Lighthouse book so users can correctly understand and configure their ENR to maximize Lighthouse's connectivity. This relies on https://github.com/sigp/discv5/pull/166 and we should wait for a new release in discv5 before adding this PR.
142 lines
5.3 KiB
Rust
142 lines
5.3 KiB
Rust
//! The main bootnode server execution.
|
|
|
|
use super::BootNodeConfig;
|
|
use lighthouse_network::{
|
|
discv5::{enr::NodeId, Discv5, Discv5Event},
|
|
EnrExt, Eth2Enr,
|
|
};
|
|
use slog::info;
|
|
use types::EthSpec;
|
|
|
|
pub async fn run<T: EthSpec>(config: BootNodeConfig<T>, log: slog::Logger) {
|
|
let BootNodeConfig {
|
|
listen_socket,
|
|
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!(
|
|
log, "Configuration parameters";
|
|
"listening_address" => %listen_socket,
|
|
"advertised_v4_address" => ?pretty_v4_socket,
|
|
"advertised_v6_address" => ?pretty_v6_socket,
|
|
"eth2" => eth2_field
|
|
);
|
|
|
|
info!(log, "Identity established"; "peer_id" => %local_enr.peer_id(), "node_id" => %local_enr.node_id());
|
|
|
|
// build the contactable multiaddr list, adding the p2p protocol
|
|
info!(log, "Contact information"; "enr" => local_enr.to_base64());
|
|
info!(log, "Contact information"; "multiaddrs" => ?local_enr.multiaddr_p2p());
|
|
|
|
// 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!(
|
|
log,
|
|
"Adding bootnode";
|
|
"ipv4_address" => ?enr.udp4_socket(),
|
|
"ipv6_address" => ?enr.udp6_socket(),
|
|
"peer_id" => ?enr.peer_id(),
|
|
"node_id" => ?enr.node_id()
|
|
);
|
|
if enr != local_enr {
|
|
if let Err(e) = discv5.add_enr(enr) {
|
|
slog::warn!(log, "Failed adding ENR"; "error" => ?e);
|
|
}
|
|
}
|
|
}
|
|
|
|
// start the server
|
|
if let Err(e) = discv5.start(listen_socket).await {
|
|
slog::crit!(log, "Could not start discv5 server"; "error" => %e);
|
|
return;
|
|
}
|
|
|
|
// if there are peers in the local routing table, establish a session by running a query
|
|
if !discv5.table_entries_id().is_empty() {
|
|
info!(log, "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) => {
|
|
slog::crit!(log, "Failed to obtain event stream"; "error" => %e);
|
|
return;
|
|
}
|
|
};
|
|
|
|
// 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!(
|
|
log, "Server metrics";
|
|
"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_nodes" => ipv6_only_reachable,
|
|
"ipv6_and_ipv4_nodes" => ipv4_ipv6_reachable,
|
|
"unreachable_nodes" => unreachable_nodes,
|
|
);
|
|
|
|
}
|
|
Some(event) = event_stream.recv() => {
|
|
match event {
|
|
Discv5Event::Discovered(_enr) => {
|
|
// An ENR has bee obtained by the server
|
|
// Ignore these events here
|
|
}
|
|
Discv5Event::EnrAdded { .. } => {} // Ignore
|
|
Discv5Event::TalkRequest(_) => {} // Ignore
|
|
Discv5Event::NodeInserted { .. } => {} // Ignore
|
|
Discv5Event::SocketUpdated(socket_addr) => {
|
|
info!(log, "Advertised socket address updated"; "socket_addr" => %socket_addr);
|
|
}
|
|
Discv5Event::SessionEstablished{ .. } => {} // Ignore
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|