Skip to content

Commit cd8d398

Browse files
committed
feat(p2p): warn when the bootnode list yields nothing to dial
Both ways a bootnode file can come to nothing were silent above `debug`: - Every entry unusable. Each rejection warns individually, but the empty list that results is indistinguishable from having configured no bootnodes at all. - Every entry `quic`-less. Each skip is logged at `debug`, which is right for one record, but a beacon-chain bootstrap list is entirely `tcp`/`udp` records: feeding one in produces zero static dials with nothing above `debug` to say so. Either case boots a node that peers with nobody unless discovery is enabled, so each now gets one line at `warn` with the count.
1 parent 70748b1 commit cd8d398

1 file changed

Lines changed: 27 additions & 2 deletions

File tree

crates/net/p2p/src/lib.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ pub fn build_swarm(
303303
.build();
304304
let local_peer_id = *swarm.local_peer_id();
305305
let mut bootnode_addrs = HashMap::new();
306+
let mut quic_less_bootnodes = 0usize;
306307
for bootnode in config.bootnodes {
307308
let peer_id = PeerId::from_public_key(&bootnode.public_key);
308309
if peer_id == local_peer_id {
@@ -311,13 +312,25 @@ pub fn build_swarm(
311312
// Discovery-only seed: reachable over discv5, but with no QUIC port
312313
// there is nothing for the swarm to dial.
313314
let Some(quic_port) = bootnode.quic_port else {
315+
quic_less_bootnodes += 1;
314316
debug!(%peer_id, ip = %bootnode.ip, "Bootnode advertises no quic port, discv5 seed only");
315317
continue;
316318
};
317319
let addr = quic_multiaddr(bootnode.ip, quic_port, peer_id);
318320
bootnode_addrs.insert(peer_id, addr.clone());
319321
swarm.dial(addr).unwrap();
320322
}
323+
// Every skip above is individually unremarkable and logged at `debug`, but a
324+
// list that produces no dial target at all leaves the node isolated unless
325+
// discovery is on, which is worth one line at `warn`. A beacon-chain
326+
// bootstrap list is exactly this shape: `tcp` and `udp`, never `quic`.
327+
if bootnode_addrs.is_empty() && quic_less_bootnodes > 0 {
328+
warn!(
329+
quic_less_bootnodes,
330+
"No bootnode advertises a quic port, so nothing will be dialed statically; \
331+
peering depends entirely on discv5 discovery"
332+
);
333+
}
321334
let addr = Multiaddr::empty()
322335
.with(config.listening_socket.ip().into())
323336
.with(Protocol::Udp(config.listening_socket.port()))
@@ -844,15 +857,27 @@ impl Bootnode {
844857
/// from booting. A record carrying only one of `quic` and `udp` is kept, since
845858
/// each is useful on its own.
846859
pub fn parse_enrs(enrs: Vec<String>) -> Vec<Bootnode> {
847-
enrs.into_iter()
860+
let configured = enrs.len();
861+
let bootnodes: Vec<Bootnode> = enrs
862+
.into_iter()
848863
.filter_map(|enr_str| {
849864
parse_enr(&enr_str)
850865
.inspect_err(
851866
|reason| warn!(%reason, enr = %enr_str, "Skipping unusable bootnode ENR"),
852867
)
853868
.ok()
854869
})
855-
.collect()
870+
.collect();
871+
// Each rejection above already warned, but a file where *every* entry is
872+
// unusable boots a node with an empty bootnode list, which otherwise looks
873+
// identical to having configured none at all.
874+
if bootnodes.is_empty() && configured > 0 {
875+
warn!(
876+
configured,
877+
"No bootnode ENR could be used; the node starts with no bootnodes"
878+
);
879+
}
880+
bootnodes
856881
}
857882

858883
fn parse_enr(enr_str: &str) -> Result<Bootnode, String> {

0 commit comments

Comments
 (0)