use super::Roving;
use crate::dht::Dht;
use crate::dht::mutable::target_of;
use crate::rendezvous::item::Call;
use crate::rendezvous::punch::local_ips;
use crate::rendezvous::say::{self, Kind};
use std::net::{IpAddr, SocketAddr, TcpStream};
use std::time::SystemTime;
impl Roving {
pub(super) fn called(
&mut self,
dht: &mut Dht,
engine: Vec<SocketAddr>,
again: bool,
) -> Result<Option<TcpStream>, String> {
let port = self.punch()?.port();
let keypair = self.pairing.inbox_keypair()?;
let salt = self.pairing.inbox_salt();
let nonce = nonce()?;
let holders = dht
.holders(target_of(&keypair.public(), &salt))
.map_err(|e| self.missed(Kind::Call, say::call_unwritten(nonce), e))?;
let call = Call {
nonce,
endpoints: listed(local_ips(), &dht.observed(), port),
};
let seq = unix_now().max(self.last_seq + 1);
let signed = keypair.sign(salt, seq, call.seal(&self.pairing.seal_key())?)?;
let acks = dht
.store(&signed, holders)
.map_err(|e| self.missed(Kind::Call, say::call_unwritten(nonce), e))?;
let line = if again {
say::recalled(nonce, &call.endpoints, acks)
} else {
say::call_written(nonce, seq, &call.endpoints, acks)
};
self.say.say(Kind::Call, line);
self.last_seq = seq;
let landed = self.punched(nonce, engine.clone())?;
if landed.is_some() {
self.cached = Some(engine);
}
Ok(landed)
}
}
pub(super) fn listed(mut ips: Vec<IpAddr>, observed: &[SocketAddr], port: u16) -> Vec<SocketAddr> {
for ip in observed.iter().map(SocketAddr::ip) {
if !ips.contains(&ip) {
ips.push(ip);
}
}
ips.into_iter()
.map(|ip| SocketAddr::new(ip, port))
.collect()
}
pub(super) fn nonce() -> Result<u64, String> {
let mut bytes = [0u8; 8];
crate::dht::random(&mut bytes)?;
Ok(u64::from_be_bytes(bytes))
}
pub(super) fn unix_now() -> i64 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.map_or(0, |d| i64::try_from(d.as_secs()).unwrap_or(i64::MAX))
}