use std::{
io::Cursor,
time::{SystemTime, UNIX_EPOCH},
};
use anyhow::Result;
use futures_util::TryStreamExt;
use ipfs_api_backend_hyper::{IpfsApi, IpfsClient};
use ipfs_api_prelude::request::KeyType;
use tokio::time::{Duration, Instant, sleep};
async fn cat_all(client: &IpfsClient, cid: &str) -> Result<Vec<u8>> {
let mut s = client.cat(cid);
let mut out = Vec::new();
while let Some(chunk) = s.try_next().await? {
out.extend_from_slice(&chunk);
}
Ok(out)
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "Proof of Concept only. Meeds a running IPFS daemon (kubo) on 127.0.0.1:5001"]
async fn ipns_mutable_roundtrip() -> Result<()> {
let client = IpfsClient::default();
let a: &[u8] = b"ipns payload A";
let b: &[u8] = b"ipns payload B (updated)";
let cid_a = client.add(Cursor::new(a)).await?.hash;
let cid_b = client.add(Cursor::new(b)).await?.hash;
assert_eq!(cat_all(&client, &cid_a).await?, a);
assert_eq!(cat_all(&client, &cid_b).await?, b);
let now_ms = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis();
let key_name = format!("ml-ipns-test-{}", now_ms);
let key_info = client.key_gen(&key_name, KeyType::Ed25519, 0).await?;
let peer_id = key_info.id;
let _pub_a = client
.name_publish(
&format!("/ipfs/{}", cid_a),
false,
None,
None,
Some(&key_name),
)
.await?;
sleep(Duration::from_millis(200)).await;
let expect_path_a = format!("/ipfs/{}", cid_a);
let deadline = Instant::now() + Duration::from_secs(10);
loop {
let res = client
.name_resolve(Some(&peer_id), false, false)
.await?;
if res.path == expect_path_a {
break;
}
if Instant::now() >= deadline {
panic!(
"IPNS did not resolve to CID A within timeout; got {}",
res.path
);
}
sleep(Duration::from_millis(250)).await;
}
let _pub_b = client
.name_publish(
&format!("/ipfs/{}", cid_b),
false,
None,
None,
Some(&key_name),
)
.await?;
sleep(Duration::from_millis(200)).await;
let expect_path_b = format!("/ipfs/{}", cid_b);
let deadline2 = Instant::now() + Duration::from_secs(10);
loop {
let res = client.name_resolve(Some(&peer_id), false, true).await?;
if res.path == expect_path_b {
break;
}
if Instant::now() >= deadline2 {
panic!(
"IPNS did not advance to CID B within timeout; got {}",
res.path
);
}
sleep(Duration::from_millis(250)).await;
}
assert_eq!(cat_all(&client, &cid_b).await?, b);
Ok(())
}