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;
debug!(
closest_count = closest.len(),
"relay acquisition: evaluating closest peers for Direct relay candidates"
);
let mut candidates: Vec<RelayCandidate> = Vec::new();
for node in &closest {
let typed = node.typed_addresses();
let direct = DhtNetworkManager::first_direct_dialable(node);
debug!(
peer = %node.peer_id.to_hex(),
typed_addresses = ?typed,
selected_direct = ?direct,
"relay acquisition: evaluated peer as relay candidate"
);
if let Some(direct) = direct {
candidates.push(RelayCandidate::new(node.peer_id, direct));
}
}
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())
}
}
}