use std::time::{SystemTime, UNIX_EPOCH};
use anyhow::Result;
use mainline::{Dht, MutableItem, SigningKey, Testnet, async_dht::AsyncDht};
use tokio::time::{Duration, Instant, sleep};
async fn mutable_roundtrip_test(
writer: AsyncDht,
reader: AsyncDht,
timeout: Duration,
delay: Duration,
poll_interval: Duration,
) -> Result<()> {
assert!(writer.bootstrapped().await, "writer failed to bootstrap");
assert!(reader.bootstrapped().await, "reader failed to bootstrap");
let signing_key = SigningKey::from_bytes(&[7u8; 32]);
let pubkey = signing_key.verifying_key().to_bytes();
let now_ms = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("clock ok")
.as_millis();
let salt_buf = format!("ml-mutable-test-{now_ms}").into_bytes();
let salt_opt: Option<&[u8]> = Some(salt_buf.as_slice());
let value: &[u8] = b"hello from mainline (mutable)";
let (item, cas) = if let Some(mr) =
writer.get_mutable_most_recent(&pubkey, salt_opt).await
{
let new_seq = mr.seq() + 1;
(
MutableItem::new(signing_key, value, new_seq, salt_opt),
Some(mr.seq()),
)
} else {
(MutableItem::new(signing_key, value, 1, salt_opt), None)
};
writer.put_mutable(item, cas).await?;
sleep(delay).await;
let start = Instant::now();
let deadline = start + timeout;
let mut got = None;
let mut iterations = 0;
loop {
iterations += 1;
if let Some(mr) =
reader.get_mutable_most_recent(&pubkey, salt_opt).await
{
got = Some(mr);
break;
}
if Instant::now() >= deadline {
break;
}
sleep(poll_interval).await;
}
let elapsed = start.elapsed();
println!(
"Poll stats: {} iterations, interval={:.3}s, total={:.3}s",
iterations,
poll_interval.as_secs_f64(),
elapsed.as_secs_f64()
);
let observed =
got.expect("reader did not observe the mutable item in time");
assert_eq!(observed.value(), value, "mutable value mismatch");
assert_eq!(observed.key(), &pubkey);
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "Proof of Concept only."]
async fn mutable_put_then_get_testnet() -> Result<()> {
let testnet = Testnet::new_async(5).await?;
let writer = Dht::builder()
.bootstrap(&testnet.bootstrap)
.build()?
.as_async();
let reader = Dht::builder()
.bootstrap(&testnet.bootstrap)
.build()?
.as_async();
mutable_roundtrip_test(
writer,
reader,
Duration::from_secs(5),
Duration::from_millis(200),
Duration::from_millis(100),
)
.await
}
#[tokio::test(flavor = "multi_thread")]
#[ignore = "Proof of Concept only."]
async fn mutable_put_then_get_mainnet() -> Result<()> {
let writer = Dht::client()?.as_async();
let reader = Dht::client()?.as_async();
mutable_roundtrip_test(
writer,
reader,
Duration::from_secs(30),
Duration::from_secs(1),
Duration::from_millis(250),
)
.await
}