Add spanning tree integration tests, fix parent ancestry propagation bug

Multi-node integration test infrastructure with 100-node random topology
convergence test (5 topology variants). Two-phase drain loop processes
Noise IK handshakes and TreeAnnounce cascades over live UDP until
quiescence.

Fix parent ancestry propagation in handle_tree_announce: when a node's
current parent updates its tree state (e.g., learning a new root from
upstream), coordinates must be recomputed even though the parent itself
didn't change. Without this, chains longer than 2 hops fail to converge.
This commit is contained in:
Johnathan Corgan
2026-02-11 00:09:11 +00:00
parent 1c2cda480d
commit 74eaeab3d4
2 changed files with 688 additions and 0 deletions
+35
View File
@@ -216,6 +216,41 @@ impl Node {
);
self.send_tree_announce_to_all().await;
} else if !self.tree_state.is_root()
&& *self.tree_state.my_declaration().parent_id() == *from
{
// Our parent's ancestry changed but we're keeping the same parent.
// Recompute our own coordinates (which derive from parent's ancestry)
// and re-announce so downstream nodes stay current.
let old_root = *self.tree_state.root();
let old_depth = self.tree_state.my_coords().depth();
let new_seq = self.tree_state.my_declaration().sequence() + 1;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
self.tree_state.set_parent(*from, new_seq, timestamp);
if let Err(e) = self.tree_state.sign_declaration(&self.identity) {
warn!(error = %e, "Failed to sign declaration after parent update");
return;
}
self.tree_state.recompute_coords();
let new_root = *self.tree_state.root();
let new_depth = self.tree_state.my_coords().depth();
if new_root != old_root || new_depth != old_depth {
info!(
parent = %from,
old_root = %old_root,
new_root = %new_root,
new_depth = new_depth,
"Parent ancestry changed, re-announcing"
);
self.send_tree_announce_to_all().await;
}
}
}