use std::net::SocketAddr;
use crate::node::OpManager;
use crate::ring::PeerKeyLocation;
pub(crate) fn select_bootstrap_gateway(
ring_connection_count: usize,
configured_gateways: &[PeerKeyLocation],
own_addr: Option<SocketAddr>,
is_excluded: impl Fn(SocketAddr) -> bool,
) -> Option<(PeerKeyLocation, SocketAddr)> {
if ring_connection_count > 0 {
return None;
}
configured_gateways.iter().find_map(|gw| {
let addr = gw.socket_addr()?;
if Some(addr) == own_addr || is_excluded(addr) {
return None;
}
Some((gw.clone(), addr))
})
}
pub(crate) fn bootstrap_gateway_target(
op_manager: &OpManager,
is_excluded: impl Fn(SocketAddr) -> bool,
) -> Option<(PeerKeyLocation, SocketAddr)> {
select_bootstrap_gateway(
op_manager.ring.connection_manager.connection_count(),
&op_manager.configured_gateways,
op_manager.ring.connection_manager.get_own_addr(),
is_excluded,
)
}
#[cfg(test)]
mod tests {
use super::*;
fn gw(port: u16) -> PeerKeyLocation {
let key = crate::transport::TransportPublicKey::from_bytes([port as u8; 32]);
let addr = SocketAddr::from(([127, 0, 0, 1], port));
PeerKeyLocation::new(key, addr)
}
#[test]
fn bootstrap_fallback_selects_gateway_when_ring_empty() {
let gateway = gw(4001);
let selected = select_bootstrap_gateway(0, std::slice::from_ref(&gateway), None, |_| false);
let (peer, addr) = selected.expect("empty ring + configured gateway must select it");
assert_eq!(peer.socket_addr(), gateway.socket_addr());
assert_eq!(addr, gateway.socket_addr().unwrap());
}
#[test]
fn bootstrap_fallback_inactive_when_ring_non_empty() {
let gateway = gw(4001);
assert!(
select_bootstrap_gateway(1, &[gateway], None, |_| false).is_none(),
"fallback must not fire with ring connections present"
);
}
#[test]
fn bootstrap_fallback_skips_self() {
let gw1 = gw(4001);
let gw2 = gw(4002);
let own_addr = gw1.socket_addr();
let selected =
select_bootstrap_gateway(0, &[gw1.clone(), gw2.clone()], own_addr, |_| false);
let (peer, _) = selected.expect("second gateway should be selected");
assert_eq!(
peer.socket_addr(),
gw2.socket_addr(),
"self gateway must be skipped"
);
assert!(
select_bootstrap_gateway(0, std::slice::from_ref(&gw1), gw1.socket_addr(), |_| false)
.is_none(),
"sole self gateway must yield no fallback"
);
}
#[test]
fn bootstrap_fallback_excludes_tried_gateways() {
let gw1 = gw(4001);
let gw2 = gw(4002);
let tried = [gw1.socket_addr().unwrap()];
let selected = select_bootstrap_gateway(0, &[gw1.clone(), gw2.clone()], None, |addr| {
tried.contains(&addr)
});
let (peer, _) = selected.expect("untried gateway should be selected");
assert_eq!(peer.socket_addr(), gw2.socket_addr());
let all_tried = [gw1.socket_addr().unwrap(), gw2.socket_addr().unwrap()];
assert!(
select_bootstrap_gateway(0, &[gw1, gw2], None, |addr| all_tried.contains(&addr))
.is_none(),
"all gateways tried must yield no fallback"
);
}
#[test]
fn bootstrap_fallback_none_when_no_gateways_configured() {
assert!(
select_bootstrap_gateway(0, &[], None, |_| false).is_none(),
"empty ring with no configured gateways must yield no fallback"
);
}
}