use std::sync::Arc;
use std::time::Duration;
use rand::Rng;
use tracing::{debug, info, warn};
use crate::dht_network_manager::DhtNetworkManager;
use crate::reachability::acquisition::{AcquiredRelay, RelayAcquisition, RelayCandidate};
use crate::transport_handle::TransportHandle;
const STARTUP_JITTER_UPPER_MS: u64 = 2000;
#[derive(Debug, Clone)]
pub(crate) enum RelayAcquisitionOutcome {
Acquired(AcquiredRelay),
Failed(String),
}
pub(crate) async fn run_relay_acquisition(
dht: &DhtNetworkManager,
transport: &Arc<TransportHandle>,
) -> RelayAcquisitionOutcome {
let jitter_ms = rand::thread_rng().gen_range(0..STARTUP_JITTER_UPPER_MS);
if jitter_ms > 0 {
tokio::time::sleep(Duration::from_millis(jitter_ms)).await;
}
let own_key = *dht.peer_id().to_bytes();
let closest = dht.find_closest_nodes_local(&own_key, dht.k_value()).await;
let candidates: Vec<RelayCandidate> = closest
.iter()
.filter_map(|node| {
let direct = DhtNetworkManager::first_direct_dialable(node)?;
Some(RelayCandidate::new(node.peer_id, direct))
})
.collect();
if candidates.is_empty() {
warn!("relay acquisition: no direct-addressable candidates in routing table");
return RelayAcquisitionOutcome::Failed(
"no direct-addressable candidates in routing table".to_string(),
);
}
debug!(
candidate_count = candidates.len(),
"relay acquisition: starting XOR-closest walk"
);
let coordinator = RelayAcquisition::new(Arc::clone(transport));
match coordinator.acquire(candidates).await {
Ok(relay) => {
info!(
relayer = ?relay.relayer,
allocated = %relay.allocated_public_addr,
"relay acquisition: session established"
);
RelayAcquisitionOutcome::Acquired(relay)
}
Err(e) => {
warn!(error = %e, "relay acquisition: all candidates failed");
RelayAcquisitionOutcome::Failed(e.to_string())
}
}
}