mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 10:22:38 +00:00
Add Experimental QUIC support (#4577)
## Issue Addressed #4402 ## Proposed Changes This PR adds QUIC support to Lighthouse. As this is not officially spec'd this will only work between lighthouse <-> lighthouse connections. We attempt a QUIC connection (if the node advertises it) and if it fails we fallback to TCP. This should be a backwards compatible modification. We want to test this functionality on live networks to observe any improvements in bandwidth/latency. NOTE: This also removes the websockets transport as I believe no one is really using it. It should be mentioned in our release however. Co-authored-by: João Oliveira <hello@jxs.pt>
This commit is contained in:
@@ -12,20 +12,49 @@ use types::EthSpec;
|
||||
|
||||
/// Configuration required to construct the UPnP port mappings.
|
||||
pub struct UPnPConfig {
|
||||
/// The local tcp port.
|
||||
/// The local TCP port.
|
||||
tcp_port: u16,
|
||||
/// The local udp port.
|
||||
udp_port: u16,
|
||||
/// The local UDP discovery port.
|
||||
disc_port: u16,
|
||||
/// The local UDP quic port.
|
||||
quic_port: u16,
|
||||
/// Whether discovery is enabled or not.
|
||||
disable_discovery: bool,
|
||||
/// Whether quic is enabled or not.
|
||||
disable_quic_support: bool,
|
||||
}
|
||||
|
||||
/// Contains mappings that managed to be established.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct EstablishedUPnPMappings {
|
||||
/// A TCP port mapping for libp2p.
|
||||
pub tcp_port: Option<u16>,
|
||||
/// A UDP port for the QUIC libp2p transport.
|
||||
pub udp_quic_port: Option<u16>,
|
||||
/// A UDP port for discv5.
|
||||
pub udp_disc_port: Option<u16>,
|
||||
}
|
||||
|
||||
impl EstablishedUPnPMappings {
|
||||
/// Returns true if at least one value is set.
|
||||
pub fn is_some(&self) -> bool {
|
||||
self.tcp_port.is_some() || self.udp_quic_port.is_some() || self.udp_disc_port.is_some()
|
||||
}
|
||||
|
||||
// Iterator over the UDP ports
|
||||
pub fn udp_ports(&self) -> impl Iterator<Item = &u16> {
|
||||
self.udp_quic_port.iter().chain(self.udp_disc_port.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl UPnPConfig {
|
||||
pub fn from_config(config: &NetworkConfig) -> Option<Self> {
|
||||
config.listen_addrs().v4().map(|v4_addr| UPnPConfig {
|
||||
tcp_port: v4_addr.tcp_port,
|
||||
udp_port: v4_addr.udp_port,
|
||||
disc_port: v4_addr.disc_port,
|
||||
quic_port: v4_addr.quic_port,
|
||||
disable_discovery: config.disable_discovery,
|
||||
disable_quic_support: config.disable_quic_support,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -68,6 +97,8 @@ pub fn construct_upnp_mappings<T: EthSpec>(
|
||||
|
||||
debug!(log, "UPnP Local IP Discovered"; "ip" => ?local_ip);
|
||||
|
||||
let mut mappings = EstablishedUPnPMappings::default();
|
||||
|
||||
match local_ip {
|
||||
IpAddr::V4(address) => {
|
||||
let libp2p_socket = SocketAddrV4::new(address, config.tcp_port);
|
||||
@@ -76,39 +107,46 @@ pub fn construct_upnp_mappings<T: EthSpec>(
|
||||
// one.
|
||||
// I've found this to be more reliable. If multiple users are behind a single
|
||||
// router, they should ideally try to set different port numbers.
|
||||
let tcp_socket = add_port_mapping(
|
||||
mappings.tcp_port = add_port_mapping(
|
||||
&gateway,
|
||||
igd::PortMappingProtocol::TCP,
|
||||
libp2p_socket,
|
||||
"tcp",
|
||||
&log,
|
||||
).and_then(|_| {
|
||||
).map(|_| {
|
||||
let external_socket = external_ip.as_ref().map(|ip| SocketAddr::new((*ip).into(), config.tcp_port)).map_err(|_| ());
|
||||
info!(log, "UPnP TCP route established"; "external_socket" => format!("{}:{}", external_socket.as_ref().map(|ip| ip.to_string()).unwrap_or_else(|_| "".into()), config.tcp_port));
|
||||
external_socket
|
||||
config.tcp_port
|
||||
}).ok();
|
||||
|
||||
let udp_socket = if !config.disable_discovery {
|
||||
let discovery_socket = SocketAddrV4::new(address, config.udp_port);
|
||||
let set_udp_mapping = |udp_port| {
|
||||
let udp_socket = SocketAddrV4::new(address, udp_port);
|
||||
add_port_mapping(
|
||||
&gateway,
|
||||
igd::PortMappingProtocol::UDP,
|
||||
discovery_socket,
|
||||
udp_socket,
|
||||
"udp",
|
||||
&log,
|
||||
).and_then(|_| {
|
||||
let external_socket = external_ip
|
||||
.map(|ip| SocketAddr::new(ip.into(), config.udp_port)).map_err(|_| ());
|
||||
info!(log, "UPnP UDP route established"; "external_socket" => format!("{}:{}", external_socket.as_ref().map(|ip| ip.to_string()).unwrap_or_else(|_| "".into()), config.udp_port));
|
||||
external_socket
|
||||
}).ok()
|
||||
} else {
|
||||
None
|
||||
).map(|_| {
|
||||
info!(log, "UPnP UDP route established"; "external_socket" => format!("{}:{}", external_ip.as_ref().map(|ip| ip.to_string()).unwrap_or_else(|_| "".into()), udp_port));
|
||||
})
|
||||
};
|
||||
|
||||
// Set the discovery UDP port mapping
|
||||
if !config.disable_discovery && set_udp_mapping(config.disc_port).is_ok() {
|
||||
mappings.udp_disc_port = Some(config.disc_port);
|
||||
}
|
||||
|
||||
// Set the quic UDP port mapping
|
||||
if !config.disable_quic_support && set_udp_mapping(config.quic_port).is_ok() {
|
||||
mappings.udp_quic_port = Some(config.quic_port);
|
||||
}
|
||||
|
||||
// report any updates to the network service.
|
||||
network_send.send(NetworkMessage::UPnPMappingEstablished{ tcp_socket, udp_socket })
|
||||
.unwrap_or_else(|e| debug!(log, "Could not send message to the network service"; "error" => %e));
|
||||
if mappings.is_some() {
|
||||
network_send.send(NetworkMessage::UPnPMappingEstablished{ mappings })
|
||||
.unwrap_or_else(|e| debug!(log, "Could not send message to the network service"; "error" => %e));
|
||||
}
|
||||
}
|
||||
_ => debug!(log, "UPnP no routes constructed. IPv6 not supported"),
|
||||
}
|
||||
@@ -161,12 +199,12 @@ fn add_port_mapping(
|
||||
}
|
||||
|
||||
/// Removes the specified TCP and UDP port mappings.
|
||||
pub fn remove_mappings(tcp_port: Option<u16>, udp_port: Option<u16>, log: &slog::Logger) {
|
||||
if tcp_port.is_some() || udp_port.is_some() {
|
||||
pub fn remove_mappings(mappings: &EstablishedUPnPMappings, log: &slog::Logger) {
|
||||
if mappings.is_some() {
|
||||
debug!(log, "Removing UPnP port mappings");
|
||||
match igd::search_gateway(Default::default()) {
|
||||
Ok(gateway) => {
|
||||
if let Some(tcp_port) = tcp_port {
|
||||
if let Some(tcp_port) = mappings.tcp_port {
|
||||
match gateway.remove_port(igd::PortMappingProtocol::TCP, tcp_port) {
|
||||
Ok(()) => debug!(log, "UPnP Removed TCP port mapping"; "port" => tcp_port),
|
||||
Err(e) => {
|
||||
@@ -174,8 +212,8 @@ pub fn remove_mappings(tcp_port: Option<u16>, udp_port: Option<u16>, log: &slog:
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(udp_port) = udp_port {
|
||||
match gateway.remove_port(igd::PortMappingProtocol::UDP, udp_port) {
|
||||
for udp_port in mappings.udp_ports() {
|
||||
match gateway.remove_port(igd::PortMappingProtocol::UDP, *udp_port) {
|
||||
Ok(()) => debug!(log, "UPnP Removed UDP port mapping"; "port" => udp_port),
|
||||
Err(e) => {
|
||||
debug!(log, "UPnP Failed to remove UDP port mapping"; "port" => udp_port, "error" => %e)
|
||||
|
||||
@@ -37,7 +37,6 @@ const VALIDATOR_COUNT: usize = SLOTS_PER_EPOCH as usize;
|
||||
const SMALL_CHAIN: u64 = 2;
|
||||
const LONG_CHAIN: u64 = SLOTS_PER_EPOCH * 2;
|
||||
|
||||
const TCP_PORT: u16 = 42;
|
||||
const SEQ_NUMBER: u64 = 0;
|
||||
|
||||
/// The default time to wait for `BeaconProcessor` events.
|
||||
@@ -195,15 +194,7 @@ impl TestRig {
|
||||
});
|
||||
let enr_key = CombinedKey::generate_secp256k1();
|
||||
let enr = EnrBuilder::new("v4").build(&enr_key).unwrap();
|
||||
let network_globals = Arc::new(NetworkGlobals::new(
|
||||
enr,
|
||||
Some(TCP_PORT),
|
||||
None,
|
||||
meta_data,
|
||||
vec![],
|
||||
false,
|
||||
&log,
|
||||
));
|
||||
let network_globals = Arc::new(NetworkGlobals::new(enr, meta_data, vec![], false, &log));
|
||||
|
||||
let executor = harness.runtime.task_executor.clone();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::sync::manager::RequestId as SyncId;
|
||||
use crate::nat::EstablishedUPnPMappings;
|
||||
use crate::network_beacon_processor::InvalidBlockStorage;
|
||||
use crate::persisted_dht::{clear_dht, load_dht, persist_dht};
|
||||
use crate::router::{Router, RouterMessage};
|
||||
@@ -26,7 +27,7 @@ use lighthouse_network::{
|
||||
MessageId, NetworkEvent, NetworkGlobals, PeerId,
|
||||
};
|
||||
use slog::{crit, debug, error, info, o, trace, warn};
|
||||
use std::{collections::HashSet, net::SocketAddr, pin::Pin, sync::Arc, time::Duration};
|
||||
use std::{collections::HashSet, pin::Pin, sync::Arc, time::Duration};
|
||||
use store::HotColdDB;
|
||||
use strum::IntoStaticStr;
|
||||
use task_executor::ShutdownReason;
|
||||
@@ -93,12 +94,10 @@ pub enum NetworkMessage<T: EthSpec> {
|
||||
/// The result of the validation
|
||||
validation_result: MessageAcceptance,
|
||||
},
|
||||
/// Called if a known external TCP socket address has been updated.
|
||||
/// Called if UPnP managed to establish an external port mapping.
|
||||
UPnPMappingEstablished {
|
||||
/// The external TCP address has been updated.
|
||||
tcp_socket: Option<SocketAddr>,
|
||||
/// The external UDP address has been updated.
|
||||
udp_socket: Option<SocketAddr>,
|
||||
/// The mappings that were established.
|
||||
mappings: EstablishedUPnPMappings,
|
||||
},
|
||||
/// Reports a peer to the peer manager for performing an action.
|
||||
ReportPeer {
|
||||
@@ -190,11 +189,8 @@ pub struct NetworkService<T: BeaconChainTypes> {
|
||||
/// A collection of global variables, accessible outside of the network service.
|
||||
network_globals: Arc<NetworkGlobals<T::EthSpec>>,
|
||||
/// Stores potentially created UPnP mappings to be removed on shutdown. (TCP port and UDP
|
||||
/// port).
|
||||
upnp_mappings: (Option<u16>, Option<u16>),
|
||||
/// Keeps track of if discovery is auto-updating or not. This is used to inform us if we should
|
||||
/// update the UDP socket of discovery if the UPnP mappings get established.
|
||||
discovery_auto_update: bool,
|
||||
/// ports).
|
||||
upnp_mappings: EstablishedUPnPMappings,
|
||||
/// A delay that expires when a new fork takes place.
|
||||
next_fork_update: Pin<Box<OptionFuture<Sleep>>>,
|
||||
/// A delay that expires when we need to subscribe to a new fork's topics.
|
||||
@@ -359,8 +355,7 @@ impl<T: BeaconChainTypes> NetworkService<T> {
|
||||
router_send,
|
||||
store,
|
||||
network_globals: network_globals.clone(),
|
||||
upnp_mappings: (None, None),
|
||||
discovery_auto_update: config.discv5_config.enr_update,
|
||||
upnp_mappings: EstablishedUPnPMappings::default(),
|
||||
next_fork_update,
|
||||
next_fork_subscriptions,
|
||||
next_unsubscribe,
|
||||
@@ -616,32 +611,18 @@ impl<T: BeaconChainTypes> NetworkService<T> {
|
||||
} => {
|
||||
self.libp2p.send_error_reponse(peer_id, id, error, reason);
|
||||
}
|
||||
NetworkMessage::UPnPMappingEstablished {
|
||||
tcp_socket,
|
||||
udp_socket,
|
||||
} => {
|
||||
self.upnp_mappings = (tcp_socket.map(|s| s.port()), udp_socket.map(|s| s.port()));
|
||||
NetworkMessage::UPnPMappingEstablished { mappings } => {
|
||||
self.upnp_mappings = mappings;
|
||||
// If there is an external TCP port update, modify our local ENR.
|
||||
if let Some(tcp_socket) = tcp_socket {
|
||||
if let Err(e) = self
|
||||
.libp2p
|
||||
.discovery_mut()
|
||||
.update_enr_tcp_port(tcp_socket.port())
|
||||
{
|
||||
if let Some(tcp_port) = self.upnp_mappings.tcp_port {
|
||||
if let Err(e) = self.libp2p.discovery_mut().update_enr_tcp_port(tcp_port) {
|
||||
warn!(self.log, "Failed to update ENR"; "error" => e);
|
||||
}
|
||||
}
|
||||
// if the discovery service is not auto-updating, update it with the
|
||||
// UPnP mappings
|
||||
if !self.discovery_auto_update {
|
||||
if let Some(udp_socket) = udp_socket {
|
||||
if let Err(e) = self
|
||||
.libp2p
|
||||
.discovery_mut()
|
||||
.update_enr_udp_socket(udp_socket)
|
||||
{
|
||||
warn!(self.log, "Failed to update ENR"; "error" => e);
|
||||
}
|
||||
// If there is an external QUIC port update, modify our local ENR.
|
||||
if let Some(quic_port) = self.upnp_mappings.udp_quic_port {
|
||||
if let Err(e) = self.libp2p.discovery_mut().update_enr_quic_port(quic_port) {
|
||||
warn!(self.log, "Failed to update ENR"; "error" => e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -994,7 +975,7 @@ impl<T: BeaconChainTypes> Drop for NetworkService<T> {
|
||||
}
|
||||
|
||||
// attempt to remove port mappings
|
||||
crate::nat::remove_mappings(self.upnp_mappings.0, self.upnp_mappings.1, &self.log);
|
||||
crate::nat::remove_mappings(&self.upnp_mappings, &self.log);
|
||||
|
||||
info!(self.log, "Network service shutdown");
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let mut config = NetworkConfig::default();
|
||||
config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, 21212, 21212);
|
||||
config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, 21212, 21212, 21213);
|
||||
config.discv5_config.table_filter = |_| true; // Do not ignore local IPs
|
||||
config.upnp_enabled = false;
|
||||
config.boot_nodes_enr = enrs.clone();
|
||||
|
||||
Reference in New Issue
Block a user