Initial release: v0.1.0
Passphrase-based peer rendezvous for iroh-gossip swarms over the public Mainline DHT. Two nodes that share a passphrase + application salt find each other and join the same gossip topic — no bootstrap server, no prior knowledge of peer addresses. The algorithm is a clock-free variant of HyParView-style partition healing on BEP 44 mutable items, with K-sharded slots, logical aging with random-tie eviction, vouching of up to V neighbors at age 1, and a two-layer AEAD record envelope. Full spec in PROTOCOL.md. Ships with: * 119 tests (unit / property / integration / doctest) all green * 100% mutation-testing catch rate (cargo-mutants) * cargo-fuzz harnesses for the parse surface * Kani proofs of no-UB / no-panic on crypto::epoch_of and wire::decode_entries across their full symbolic input spaces * Stateright distributed-system model: safety invariants S1 + S2 proved exhaustively for N ≤ 6, K ≤ 3; liveness witness under weak fairness * Miri green on the synchronous modules * cargo-deny + cargo-audit clean * criterion benches * Full-matrix CI (Linux + macOS + Windows × stable + MSRV 1.93.0) * Dedicated nightly lane for Miri / mutation / fuzz / Kani / Stateright Pinned iroh 0.98.x. License MIT. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
54 files changed
+16897
No files matched your search
@@ -0,0 +1,65 @@
|
||||
//! Smoke tests for the public API surface — compile-and-dispatch checks
|
||||
//! that the public types are shaped as advertised. No network required.
|
||||
|
||||
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
||||
|
||||
use iroh_gossip_rendezvous::{Builder, Error, Rendezvous};
|
||||
|
||||
/// `Rendezvous::join` rejects an empty passphrase before any network IO.
|
||||
#[tokio::test]
|
||||
async fn join_empty_passphrase_errors() {
|
||||
let r = Rendezvous::join("", "my-app/v1").await;
|
||||
assert!(matches!(r, Err(Error::EmptyPassphrase)));
|
||||
}
|
||||
|
||||
/// Same for an empty app_salt.
|
||||
#[tokio::test]
|
||||
async fn join_empty_app_salt_errors() {
|
||||
let r = Rendezvous::join("passphrase", "").await;
|
||||
assert!(matches!(r, Err(Error::EmptyAppSalt)));
|
||||
}
|
||||
|
||||
/// The builder enforces the same checks.
|
||||
#[tokio::test]
|
||||
async fn builder_requires_passphrase() {
|
||||
let r = Builder::default().app_salt("my-app/v1").build().await;
|
||||
assert!(matches!(r, Err(Error::EmptyPassphrase)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn builder_requires_app_salt() {
|
||||
let r = Builder::default().passphrase("pass").build().await;
|
||||
assert!(matches!(r, Err(Error::EmptyAppSalt)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn builder_rejects_zero_shards() {
|
||||
let r = Builder::default()
|
||||
.passphrase("pass")
|
||||
.app_salt("my-app/v1")
|
||||
.shards(0)
|
||||
.build()
|
||||
.await;
|
||||
assert!(matches!(r, Err(Error::InvalidShardCount)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn builder_rejects_zero_max_age() {
|
||||
let r = Builder::default()
|
||||
.passphrase("pass")
|
||||
.app_salt("my-app/v1")
|
||||
.max_age(0)
|
||||
.build()
|
||||
.await;
|
||||
assert!(matches!(r, Err(Error::InvalidMaxAge)));
|
||||
}
|
||||
|
||||
/// Public re-exports compile as documented.
|
||||
#[test]
|
||||
fn public_types_exist() {
|
||||
fn assert_send<T: Send>() {}
|
||||
fn assert_sync<T: Sync>() {}
|
||||
assert_send::<Rendezvous>();
|
||||
assert_sync::<Rendezvous>();
|
||||
assert_send::<Builder>();
|
||||
}
|
||||
Reference in new issue
Block a user