use std::time::{Duration, SystemTime};
use agent_uri::AgentUri;
use agent_uri_attestation::SigningKey;
use agent_uri_dht::{Dht, DhtError, Mutation, MutationProof, Query, WriteOptions};
use crate::Libp2pDht;
const RENEW_AT: u32 = 2;
pub async fn renew(
dht: &Libp2pDht,
agent_uri: &AgentUri,
key: &SigningKey,
ttl: Duration,
options: WriteOptions,
) -> Result<(), DhtError> {
let query = Query::exact(
agent_uri.trust_root().clone(),
agent_uri.capability_path().clone(),
);
let current = dht
.lookup(&query, &agent_uri_dht::ReadOptions::default())
.await?
.into_items()
.into_iter()
.find(|record| record.agent_uri() == agent_uri)
.ok_or_else(|| DhtError::not_found(agent_uri.canonical()))?;
let expires_at = SystemTime::now() + ttl;
let proof = MutationProof::sign_next(key, ¤t, &Mutation::Refresh { ttl, expires_at });
dht.refresh(agent_uri, ttl, expires_at, &proof, options)
.await?;
Ok(())
}
#[must_use]
pub fn keep_alive(dht: Libp2pDht, agent_uri: AgentUri, key: SigningKey, ttl: Duration) -> Renewal {
let handle = tokio::spawn(async move {
let mut ticker = tokio::time::interval(ttl / RENEW_AT);
ticker.tick().await;
loop {
ticker.tick().await;
if let Err(error) = renew(&dht, &agent_uri, &key, ttl, WriteOptions::default()).await {
tracing::warn!(agent = %agent_uri, %error, "could not renew registration");
}
}
});
Renewal { handle }
}
#[derive(Debug)]
pub struct Renewal {
handle: tokio::task::JoinHandle<()>,
}
impl Drop for Renewal {
fn drop(&mut self) {
self.handle.abort();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn renewal_happens_with_a_full_ttl_left_to_retry_in() {
let ttl = Duration::from_hours(1);
assert!(ttl / RENEW_AT < ttl);
assert_eq!(ttl / RENEW_AT, Duration::from_mins(30));
}
}