Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fd10b63
Add co-author to mergify commits (#7993)
jimmygchen Sep 4, 2025
148997b
fix TOCTOU vulnerability in unused_port module
sashaodessa Sep 9, 2025
b817851
Update engine methods in notifier (#8038)
jimmygchen Sep 14, 2025
f04d5ec
Another check to prevent duplicate block imports (#8050)
michaelsproul Sep 16, 2025
4409500
Remove column reconstruction when processing rpc requests (#8051)
eserilev Sep 16, 2025
aba3627
Reduce reconstruction queue capacity (#8053)
eserilev Sep 16, 2025
242bdfc
Add instrumentation to `recompute_head_at_slot` (#8049)
eserilev Sep 16, 2025
3de646c
Enable reconstruction for nodes custodying more than 50% of columns a…
jimmygchen Sep 16, 2025
191570e
chore: Bump discv5 and remove generic DefaultProtocolId in metrics (#…
jking-aus Sep 16, 2025
b7d78a9
Don't penalize peers for extending ignored chains (#8042)
dapplion Sep 17, 2025
5928407
fix(rate_limiter): add missing prune calls for light client protocols…
gitToki Sep 17, 2025
3cb7e59
Update issue template (#7938)
jimmygchen Sep 18, 2025
521be2b
Prevent silently dropping cell proof chunks (#8023)
eserilev Sep 18, 2025
684632d
Fix reprocess queue memory leak (#8065)
michaelsproul Sep 18, 2025
3543a20
Add experimental complete-blob-backfill flag (#7751)
michaelsproul Sep 18, 2025
92f60b8
Add release helper script to list PRs and breaking changes (#7737)
jimmygchen Sep 18, 2025
51321da
Make the block cache optional (#8066)
michaelsproul Sep 18, 2025
4111bcb
Use scoped rayon pool for backfill chain segment processing (#7924)
jimmygchen Sep 18, 2025
78d330e
Consolidate `reqresp_pre_import_cache` into `data_availability_check…
jimmygchen Sep 19, 2025
4efe47b
Rename `--subscribe-all-data-column-subnets` to `--supernode` and mak…
jimmygchen Sep 19, 2025
366fb0e
Always upload sim test logs (#8082)
jimmygchen Sep 19, 2025
c1fb060
Merge remote-tracking branch 'origin/stable' into unstable
michaelsproul Sep 22, 2025
1dbc4f8
Refine HTTP status logs (#8098)
michaelsproul Sep 22, 2025
7a7fe96
Reduce `TARGET_BACKFILL_SLOTS` in checkpoint sync test (#8102)
eserilev Sep 23, 2025
d80c0ff
Use HTTPS for xdelta3 in Cargo.toml (#8094)
aviggiano Sep 24, 2025
af27402
Run reconstruction inside a scoped rayon pool (#8075)
eserilev Sep 24, 2025
79b3321
Only send data coumn subnet discovery requests after peerdas is sched…
jimmygchen Sep 25, 2025
ffa7b2b
Only mark block lookups as pending if block is importing from gossip …
dapplion Sep 25, 2025
20c6ce4
Fulu testnet configs (#8117)
eserilev Sep 26, 2025
c754234
Fix bugs in proposer calculation post-Fulu (#8101)
michaelsproul Sep 26, 2025
edcfee6
Fix bug in fork calculation at fork boundaries (#8121)
pawanjay176 Sep 28, 2025
38fdaf7
Fix proposer shuffling decision slot at boundary (#8128)
michaelsproul Sep 29, 2025
e5b4983
Release v8.0.0 rc.0 (#8127)
jimmygchen Sep 29, 2025
9c6d331
Update book for DB schema v28 (#8132)
michaelsproul Sep 30, 2025
af5cbfb
Bump superstruct to `0.10.0` (#8133)
macladson Sep 30, 2025
26575c5
Improve spec compliance for `/eth/v1/config/spec` API (#8144)
michaelsproul Oct 1, 2025
ff8b514
Remove unnecessary warning logs and update logging levels (#8145)
jimmygchen Oct 6, 2025
4eb8960
Fulu ASCII art (#8151)
eserilev Oct 7, 2025
a4ad3e4
Fallback to getPayload v1 if v2 fails (#8163)
pawanjay176 Oct 7, 2025
b5c2a96
Quote `BeaconState::proposer_lookahead` in JSON repr (#8167)
michaelsproul Oct 8, 2025
2a433bc
Remove deprecated CLI flags and references for v8.0.0 (#8142)
jimmygchen Oct 8, 2025
13dfa92
Block proposal optimisations (#8156)
michaelsproul Oct 8, 2025
8e382ce
Bump kzg library versions (#8174)
pawanjay176 Oct 9, 2025
3110ca3
Implement `/eth/v1/beacon/blobs` endpoint (#8103)
chong-he Oct 9, 2025
538b704
Reject data columns that does not descend from finalize root instead …
jimmygchen Oct 9, 2025
0c9fdea
Update `ForkName::latest_stable` to Fulu for tests (#8181)
michaelsproul Oct 9, 2025
5825bfa
Merge branch 'stable' into unstable
sashaodessa Oct 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/mergify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ queue_rules:


{{ body | get_section("## Proposed Changes", "") }}

{% for commit in commits | unique(attribute='email_author') %}
Co-Authored-By: {{ commit.author }} <{{ commit.email_author }}>
{% endfor %}
Comment on lines +108 to +111
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these changes relevant?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No this is some leftover stuff from stable that hasn't been backmerged to unstable yet (see https://github.com/sigp/lighthouse/commits/stable/), it should be deleted from this PR.

queue_conditions:
- "#approved-reviews-by >= 1"
- "check-success=license/cla"
Expand Down
30 changes: 24 additions & 6 deletions beacon_node/lighthouse_network/src/listen_addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,37 @@ impl ListenAddress {
pub fn unused_v4_ports() -> Self {
ListenAddress::V4(ListenAddr {
addr: Ipv4Addr::UNSPECIFIED,
disc_port: unused_port::unused_udp4_port().unwrap(),
quic_port: unused_port::unused_udp4_port().unwrap(),
tcp_port: unused_port::unused_tcp4_port().unwrap(),
disc_port: 0,
quic_port: 0,
tcp_port: 0,
})
}

#[cfg(test)]
pub fn unused_v6_ports() -> Self {
ListenAddress::V6(ListenAddr {
addr: Ipv6Addr::UNSPECIFIED,
disc_port: unused_port::unused_udp6_port().unwrap(),
quic_port: unused_port::unused_udp6_port().unwrap(),
tcp_port: unused_port::unused_tcp6_port().unwrap(),
disc_port: 0,
quic_port: 0,
tcp_port: 0,
})
}
}

/// Compute the UDP discovery port given flags and TCP port.
pub fn compute_discovery_port(use_zero_ports: bool, tcp_port: u16, maybe_disc_port: Option<u16>) -> u16 {
if use_zero_ports {
0
} else {
maybe_disc_port.unwrap_or(tcp_port)
}
}

/// Compute the UDP QUIC port given flags and TCP port.
pub fn compute_quic_port(use_zero_ports: bool, tcp_port: u16, maybe_quic_port: Option<u16>) -> u16 {
if use_zero_ports {
0
} else {
maybe_quic_port.unwrap_or(if tcp_port == 0 { 0 } else { tcp_port + 1 })
}
}
110 changes: 45 additions & 65 deletions beacon_node/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1020,10 +1020,7 @@ pub fn parse_listening_addresses(cli_args: &ArgMatches) -> Result<ListenAddress,
let port = maybe_port6.unwrap_or(port);

// use zero ports if required. If not, use the given port.
let tcp_port = use_zero_ports
.then(unused_port::unused_tcp6_port)
.transpose()?
.unwrap_or(port);
let tcp_port = if use_zero_ports { 0 } else { port };

if maybe_disc6_port.is_some() {
warn!("When listening only over IPv6, use the --discovery-port flag. The value of --discovery-port6 will be ignored.")
Expand All @@ -1035,17 +1032,17 @@ pub fn parse_listening_addresses(cli_args: &ArgMatches) -> Result<ListenAddress,

// use zero ports if required. If not, use the specific udp port. If none given, use
// the tcp port.
let disc_port = use_zero_ports
.then(unused_port::unused_udp6_port)
.transpose()?
.or(maybe_disc_port)
.unwrap_or(tcp_port);

let quic_port = use_zero_ports
.then(unused_port::unused_udp6_port)
.transpose()?
.or(maybe_quic_port)
.unwrap_or(if tcp_port == 0 { 0 } else { tcp_port + 1 });
let disc_port = lighthouse_network::listen_addr::compute_discovery_port(
use_zero_ports,
tcp_port,
maybe_disc_port,
);

let quic_port = lighthouse_network::listen_addr::compute_quic_port(
use_zero_ports,
tcp_port,
maybe_quic_port,
);

ListenAddress::V6(lighthouse_network::ListenAddr {
addr: ipv6,
Expand All @@ -1058,24 +1055,21 @@ pub fn parse_listening_addresses(cli_args: &ArgMatches) -> Result<ListenAddress,
// A single ipv4 address was provided. Set the ports

// use zero ports if required. If not, use the given port.
let tcp_port = use_zero_ports
.then(unused_port::unused_tcp4_port)
.transpose()?
.unwrap_or(port);
let tcp_port = if use_zero_ports { 0 } else { port };
// use zero ports if required. If not, use the specific discovery port. If none given, use
// the tcp port.
let disc_port = use_zero_ports
.then(unused_port::unused_udp4_port)
.transpose()?
.or(maybe_disc_port)
.unwrap_or(tcp_port);
let disc_port = lighthouse_network::listen_addr::compute_discovery_port(
use_zero_ports,
tcp_port,
maybe_disc_port,
);
// use zero ports if required. If not, use the specific quic port. If none given, use
// the tcp port + 1.
let quic_port = use_zero_ports
.then(unused_port::unused_udp4_port)
.transpose()?
.or(maybe_quic_port)
.unwrap_or(if tcp_port == 0 { 0 } else { tcp_port + 1 });
let quic_port = lighthouse_network::listen_addr::compute_quic_port(
use_zero_ports,
tcp_port,
maybe_quic_port,
);

ListenAddress::V4(lighthouse_network::ListenAddr {
addr: ipv4,
Expand All @@ -1088,44 +1082,30 @@ pub fn parse_listening_addresses(cli_args: &ArgMatches) -> Result<ListenAddress,
// If --port6 is not set, we use --port
let port6 = maybe_port6.unwrap_or(port);

let ipv4_tcp_port = use_zero_ports
.then(unused_port::unused_tcp4_port)
.transpose()?
.unwrap_or(port);
let ipv4_disc_port = use_zero_ports
.then(unused_port::unused_udp4_port)
.transpose()?
.or(maybe_disc_port)
.unwrap_or(ipv4_tcp_port);
let ipv4_quic_port = use_zero_ports
.then(unused_port::unused_udp4_port)
.transpose()?
.or(maybe_quic_port)
.unwrap_or(if ipv4_tcp_port == 0 {
0
} else {
ipv4_tcp_port + 1
});
let ipv4_tcp_port = if use_zero_ports { 0 } else { port };
let ipv4_disc_port = lighthouse_network::listen_addr::compute_discovery_port(
use_zero_ports,
ipv4_tcp_port,
maybe_disc_port,
);
let ipv4_quic_port = lighthouse_network::listen_addr::compute_quic_port(
use_zero_ports,
ipv4_tcp_port,
maybe_quic_port,
);

// Defaults to 9000 when required
let ipv6_tcp_port = use_zero_ports
.then(unused_port::unused_tcp6_port)
.transpose()?
.unwrap_or(port6);
let ipv6_disc_port = use_zero_ports
.then(unused_port::unused_udp6_port)
.transpose()?
.or(maybe_disc6_port)
.unwrap_or(ipv6_tcp_port);
let ipv6_quic_port = use_zero_ports
.then(unused_port::unused_udp6_port)
.transpose()?
.or(maybe_quic6_port)
.unwrap_or(if ipv6_tcp_port == 0 {
0
} else {
ipv6_tcp_port + 1
});
let ipv6_tcp_port = if use_zero_ports { 0 } else { port6 };
let ipv6_disc_port = lighthouse_network::listen_addr::compute_discovery_port(
use_zero_ports,
ipv6_tcp_port,
maybe_disc6_port,
);
let ipv6_quic_port = lighthouse_network::listen_addr::compute_quic_port(
use_zero_ports,
ipv6_tcp_port,
maybe_quic6_port,
);

ListenAddress::DualStack(
lighthouse_network::ListenAddr {
Expand Down
33 changes: 33 additions & 0 deletions common/unused_port/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,57 @@ static FOUND_PORTS_CACHE: LazyLock<Mutex<LRUTimeCache<u16>>> =
LazyLock::new(|| Mutex::new(LRUTimeCache::new(CACHED_PORTS_TTL)));

/// A convenience wrapper over [`zero_port`].
#[deprecated(note = "Use bind_tcp4_any() which returns a bound socket to avoid TOCTOU")]
pub fn unused_tcp4_port() -> Result<u16, String> {
zero_port(Transport::Tcp, IpVersion::Ipv4)
}

/// A convenience wrapper over [`zero_port`].
#[deprecated(note = "Use bind_udp4_any() which returns a bound socket to avoid TOCTOU")]
pub fn unused_udp4_port() -> Result<u16, String> {
zero_port(Transport::Udp, IpVersion::Ipv4)
}

/// A convenience wrapper over [`zero_port`].
#[deprecated(note = "Use bind_tcp6_any() which returns a bound socket to avoid TOCTOU")]
pub fn unused_tcp6_port() -> Result<u16, String> {
zero_port(Transport::Tcp, IpVersion::Ipv6)
}

/// A convenience wrapper over [`zero_port`].
#[deprecated(note = "Use bind_udp6_any() which returns a bound socket to avoid TOCTOU")]
pub fn unused_udp6_port() -> Result<u16, String> {
zero_port(Transport::Udp, IpVersion::Ipv6)
}

/// Bind a TCPv4 listener on localhost with an ephemeral port (port 0) and return it.
/// Safe against TOCTOU: the socket remains open and reserved by the OS.
pub fn bind_tcp4_any() -> Result<TcpListener, String> {
let addr = std::net::SocketAddr::new(std::net::Ipv4Addr::LOCALHOST.into(), 0);
TcpListener::bind(addr).map_err(|e| format!("Failed to bind TCPv4 listener: {:?}", e))
}

/// Bind a TCPv6 listener on localhost with an ephemeral port (port 0) and return it.
/// Safe against TOCTOU: the socket remains open and reserved by the OS.
pub fn bind_tcp6_any() -> Result<TcpListener, String> {
let addr = std::net::SocketAddr::new(std::net::Ipv6Addr::LOCALHOST.into(), 0);
TcpListener::bind(addr).map_err(|e| format!("Failed to bind TCPv6 listener: {:?}", e))
}

/// Bind a UDPv4 socket on localhost with an ephemeral port (port 0) and return it.
/// Safe against TOCTOU: the socket remains open and reserved by the OS.
pub fn bind_udp4_any() -> Result<UdpSocket, String> {
let addr = std::net::SocketAddr::new(std::net::Ipv4Addr::LOCALHOST.into(), 0);
UdpSocket::bind(addr).map_err(|e| format!("Failed to bind UDPv4 socket: {:?}", e))
}

/// Bind a UDPv6 socket on localhost with an ephemeral port (port 0) and return it.
/// Safe against TOCTOU: the socket remains open and reserved by the OS.
pub fn bind_udp6_any() -> Result<UdpSocket, String> {
let addr = std::net::SocketAddr::new(std::net::Ipv6Addr::LOCALHOST.into(), 0);
UdpSocket::bind(addr).map_err(|e| format!("Failed to bind UDPv6 socket: {:?}", e))
}

/// A bit of hack to find an unused port.
///
/// Does not guarantee that the given port is unused after the function exits, just that it was
Expand All @@ -51,6 +83,7 @@ pub fn unused_udp6_port() -> Result<u16, String> {
/// It is possible that users are unable to bind to the ports returned by this function as the OS
/// has a buffer period where it doesn't allow binding to the same port even after the socket is
/// closed. We might have to use SO_REUSEADDR socket option from `std::net2` crate in that case.
#[deprecated(note = "Use bind_*_any() functions that return a bound socket to avoid TOCTOU")]
pub fn zero_port(transport: Transport, ipv: IpVersion) -> Result<u16, String> {
let localhost = match ipv {
IpVersion::Ipv4 => std::net::Ipv4Addr::LOCALHOST.into(),
Expand Down
23 changes: 14 additions & 9 deletions lighthouse/tests/beacon_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use std::time::Duration;
use tempfile::TempDir;
use types::non_zero_usize::new_non_zero_usize;
use types::{Address, Checkpoint, Epoch, Hash256, MainnetEthSpec};
use unused_port::{unused_tcp4_port, unused_tcp6_port, unused_udp4_port, unused_udp6_port};

const DEFAULT_EXECUTION_ENDPOINT: &str = "http://localhost:8551/";
const DEFAULT_EXECUTION_JWT_SECRET_KEY: &str =
Expand All @@ -32,6 +31,12 @@ const DUMMY_ENR_TCP_PORT: u16 = 7777;
const DUMMY_ENR_UDP_PORT: u16 = 8888;
const DUMMY_ENR_QUIC_PORT: u16 = 9999;

// Fixed test ports for config-only assertions (no actual bind occurs in these tests).
const TEST_TCP4_PORT: u16 = 39001;
const TEST_TCP6_PORT: u16 = 39011;
const TEST_UDP4_PORT: u16 = 39002;
const TEST_UDP6_PORT: u16 = 39012;

const _: () =
assert!(DUMMY_ENR_QUIC_PORT != 0 && DUMMY_ENR_TCP_PORT != 0 && DUMMY_ENR_UDP_PORT != 0);

Expand Down Expand Up @@ -1039,8 +1044,8 @@ fn network_port_flag_over_ipv4_and_ipv6() {
);
});

let port = unused_tcp4_port().expect("Unable to find unused port.");
let port6 = unused_tcp6_port().expect("Unable to find unused port.");
let port = TEST_TCP4_PORT;
let port6 = TEST_TCP6_PORT;
CommandLineTest::new()
.flag("listen-address", Some("127.0.0.1"))
.flag("listen-address", Some("::1"))
Expand Down Expand Up @@ -1422,8 +1427,8 @@ fn enr_match_flag_over_ipv6() {
const ADDR: &str = "::1";
let addr = ADDR.parse::<Ipv6Addr>().unwrap();

let udp6_port = unused_udp6_port().expect("Unable to find unused port.");
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port.");
let udp6_port = TEST_UDP6_PORT;
let tcp6_port = TEST_TCP6_PORT;

CommandLineTest::new()
.flag("enr-match", None)
Expand Down Expand Up @@ -1452,13 +1457,13 @@ fn enr_match_flag_over_ipv6() {
fn enr_match_flag_over_ipv4_and_ipv6() {
const IPV6_ADDR: &str = "::1";

let udp6_port = unused_udp6_port().expect("Unable to find unused port.");
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port.");
let udp6_port = TEST_UDP6_PORT;
let tcp6_port = TEST_TCP6_PORT;
let ipv6_addr = IPV6_ADDR.parse::<Ipv6Addr>().unwrap();

const IPV4_ADDR: &str = "127.0.0.1";
let udp4_port = unused_udp4_port().expect("Unable to find unused port.");
let tcp4_port = unused_tcp4_port().expect("Unable to find unused port.");
let udp4_port = TEST_UDP4_PORT;
let tcp4_port = TEST_TCP4_PORT;
let ipv4_addr = IPV4_ADDR.parse::<Ipv4Addr>().unwrap();

CommandLineTest::new()
Expand Down
8 changes: 5 additions & 3 deletions lighthouse/tests/boot_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
use tempfile::TempDir;
use unused_port::unused_udp4_port;

const IP_ADDRESS: &str = "192.168.2.108";

/ Fixed test port for config-only assertions (no actual bind occurs in these tests).
const TEST_UDP4_PORT: u16 = 39102;

/// Returns the `lighthouse boot_node` command.
fn base_cmd() -> Command {
let lighthouse_bin = env!("CARGO_BIN_EXE_lighthouse");
Expand Down Expand Up @@ -62,7 +64,7 @@ fn enr_address_arg() {

#[test]
fn port_flag() {
let port = unused_udp4_port().unwrap();
let port = TEST_UDP4_PORT;
CommandLineTest::new()
.flag("port", Some(port.to_string().as_str()))
.run_with_ip()
Expand Down Expand Up @@ -134,7 +136,7 @@ fn boot_nodes_flag() {

#[test]
fn enr_port_flag() {
let port = unused_udp4_port().unwrap();
let port = TEST_UDP4_PORT;
CommandLineTest::new()
.flag("enr-port", Some(port.to_string().as_str()))
.run_with_ip()
Expand Down
Loading