#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use super::TestHarness;
use ant_node::replication::audit::AuditTickResult;
use serial_test::serial;
async fn commit_and_seed(
harness: &TestHarness,
a_idx: usize,
b_idx: usize,
n: usize,
) -> Vec<[u8; 32]> {
let a = harness.test_node(a_idx).expect("node a");
let b = harness.test_node(b_idx).expect("node b");
let a_store = a.ant_protocol.as_ref().expect("a protocol").storage();
let b_store = b.ant_protocol.as_ref().expect("b protocol").storage();
let mut addrs = Vec::with_capacity(n);
for i in 0..n {
let content = format!("subtree-audit-testnet-chunk-{i}").into_bytes();
let address = *blake3::hash(&content).as_bytes();
a_store.put(&address, &content).await.expect("put on a");
b_store.put(&address, &content).await.expect("put on b");
addrs.push(address);
}
let a_engine = a.replication_engine.as_ref().expect("a engine");
a_engine
.rebuild_commitment_now()
.await
.expect("a rebuild commitment");
let a_peer = *a.p2p_node.as_ref().expect("a p2p").peer_id();
let a_commitment = a_engine
.commitment_state()
.current()
.expect("a has a current commitment")
.commitment()
.clone();
let b_engine = b.replication_engine.as_ref().expect("b engine");
b_engine
.inject_peer_commitment_for_test(&a_peer, a_commitment)
.await;
addrs
}
#[tokio::test]
#[serial]
async fn honest_node_passes_subtree_audit() {
let harness = TestHarness::setup_small().await.expect("setup");
harness.warmup_dht().await.expect("warmup");
let (a_idx, b_idx) = (3, 4);
commit_and_seed(&harness, a_idx, b_idx, 64).await;
let a_peer = *harness
.test_node(a_idx)
.expect("a")
.p2p_node
.as_ref()
.expect("a p2p")
.peer_id();
let b_engine = harness
.test_node(b_idx)
.expect("b")
.replication_engine
.as_ref()
.expect("b engine");
let result = b_engine.audit_peer_now(&a_peer).await;
assert!(
matches!(result, AuditTickResult::Passed { keys_checked, .. } if keys_checked >= 1),
"honest node must pass with at least one byte-verified leaf, got {result:?}"
);
harness.teardown().await.expect("teardown");
}
#[tokio::test]
#[serial]
async fn data_deleting_node_fails_subtree_audit() {
let harness = TestHarness::setup_small().await.expect("setup");
harness.warmup_dht().await.expect("warmup");
let (a_idx, b_idx) = (5, 6);
let addrs = commit_and_seed(&harness, a_idx, b_idx, 64).await;
let a_store = harness
.test_node(a_idx)
.expect("a")
.ant_protocol
.as_ref()
.expect("a protocol")
.storage();
for addr in &addrs {
a_store.delete(addr).await.expect("delete on adversary");
}
let a_peer = *harness
.test_node(a_idx)
.expect("a")
.p2p_node
.as_ref()
.expect("a p2p")
.peer_id();
let b_engine = harness
.test_node(b_idx)
.expect("b")
.replication_engine
.as_ref()
.expect("b engine");
let result = b_engine.audit_peer_now(&a_peer).await;
assert!(
matches!(result, AuditTickResult::Failed { .. }),
"a node that deleted its committed data must FAIL the audit, got {result:?}"
);
harness.teardown().await.expect("teardown");
}
#[tokio::test]
#[serial]
async fn honest_node_never_false_fails_across_repeated_audits() {
let harness = TestHarness::setup_small().await.expect("setup");
harness.warmup_dht().await.expect("warmup");
let (a_idx, b_idx) = (7, 8);
commit_and_seed(&harness, a_idx, b_idx, 100).await;
let a_peer = *harness
.test_node(a_idx)
.expect("a")
.p2p_node
.as_ref()
.expect("a p2p")
.peer_id();
let b_engine = harness
.test_node(b_idx)
.expect("b")
.replication_engine
.as_ref()
.expect("b engine");
for round in 0..8 {
let result = b_engine.audit_peer_now(&a_peer).await;
assert!(
!matches!(result, AuditTickResult::Failed { .. }),
"honest node false-failed on round {round}: {result:?}"
);
}
harness.teardown().await.expect("teardown");
}