use ant_protocol::transport::{MultiAddr, P2PNode, PeerId};
use std::net::{IpAddr, SocketAddr};
use std::sync::Arc;
use tracing::debug;
pub(crate) async fn record_peer_outcome(
node: &Arc<P2PNode>,
peer_id: PeerId,
addrs: &[MultiAddr],
success: bool,
rtt_ms: Option<u64>,
) {
if success {
let before = node.cached_peer_count().await;
let _ = node.add_discovered_peer(peer_id, addrs.to_vec()).await;
let after = node.cached_peer_count().await;
if after > before {
debug!("Bootstrap cache grew: {before} -> {after} peers");
}
}
if let Some(primary) = select_primary_multiaddr(addrs) {
let _ = node
.update_peer_metrics(primary, success, rtt_ms, None)
.await;
}
}
fn select_primary_multiaddr(addrs: &[MultiAddr]) -> Option<&MultiAddr> {
addrs
.iter()
.find(|a| a.socket_addr().is_some_and(|sa| is_globally_routable(&sa)))
.or_else(|| addrs.iter().find(|a| a.socket_addr().is_some()))
}
fn is_globally_routable(addr: &SocketAddr) -> bool {
match addr.ip() {
IpAddr::V4(v4) => {
!v4.is_private()
&& !v4.is_loopback()
&& !v4.is_link_local()
&& !v4.is_broadcast()
&& !v4.is_documentation()
&& !v4.is_unspecified()
}
IpAddr::V6(v6) => {
!v6.is_loopback()
&& !v6.is_unspecified()
&& !v6.is_multicast()
&& !v6.segments()[0].eq(&0xfe80) && !matches!(v6.segments()[0] & 0xfe00, 0xfc00) }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::{Ipv4Addr, Ipv6Addr};
#[test]
fn globally_routable_v4() {
assert!(is_globally_routable(&SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)),
80
)));
assert!(!is_globally_routable(&SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(10, 0, 0, 5)),
80
)));
assert!(!is_globally_routable(&SocketAddr::new(
IpAddr::V4(Ipv4Addr::LOCALHOST),
80
)));
assert!(!is_globally_routable(&SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
80
)));
assert!(!is_globally_routable(&SocketAddr::new(
IpAddr::V4(Ipv4Addr::new(203, 0, 113, 1)),
80
)));
}
#[test]
fn globally_routable_v6() {
assert!(is_globally_routable(&SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111)),
80
)));
assert!(!is_globally_routable(&SocketAddr::new(
IpAddr::V6(Ipv6Addr::LOCALHOST),
80
)));
assert!(!is_globally_routable(&SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)),
80
)));
assert!(!is_globally_routable(&SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 1)),
80
)));
}
}