node: embed the handshake leg in the peer machine, drop the connections map

The pending-handshake PeerConnection map and the per-peer control
machine map were parallel LinkId-keyed structures whose keysets must
stay coherent by hand. With every leg now born with a machine, the leg
becomes storage inside its machine (leg: Option<PeerConnection>, pure
storage the machine never reads or drives) and Node loses the
connections field; every access routes through the machine.

The non-mechanical lowerings, each argued at the site: the
rekey-vs-establish gate in handle_msg2 tests leg-absence (an established
peer's machine stays keyed by its link, so machine-presence would
misclassify every rekey msg2 as a fresh establish); the
connecting-predicates, peering observation, and handshake-slot budget
iterate machines-with-legs so connect-window machines (leg not yet
born) are excluded exactly as before and never double-counted against
their pending-connect slot; the cross-connection extract takes the leg
before disposing the machine; the stale reaper takes the leg and leaves
the machine untouched when none is present, matching the old early
return. The map-coherence debug check keeps its machine-has-carrier
direction with the embedded leg as a carrier; the leg-to-machine
direction is now true by construction and its gate const is gone.

connection_count() counts machines with legs; the connections()
iterator, the test seams, and the control-socket connection rows are
re-implemented over the embedded legs with unchanged output.
This commit is contained in:
Johnathan Corgan
2026-07-17 04:27:53 +00:00
parent 5ccd95cf3f
commit cf1c957336
12 changed files with 237 additions and 172 deletions
+38 -31
View File
@@ -372,7 +372,7 @@ impl Node {
}
fn is_connecting_to_peer(&self, peer_node_addr: &NodeAddr) -> bool {
self.connections.values().any(|conn| {
self.connections().any(|conn| {
conn.expected_identity()
.map(|id| id.node_addr() == peer_node_addr)
.unwrap_or(false)
@@ -385,7 +385,7 @@ impl Node {
transport_id: TransportId,
remote_addr: &TransportAddr,
) -> bool {
self.connections.values().any(|conn| {
self.connections().any(|conn| {
conn.expected_identity()
.map(|id| id.node_addr() == peer_node_addr)
.unwrap_or(false)
@@ -559,14 +559,14 @@ impl Node {
/// Prepare an outbound Noise msg1 at dial: allocate the session index, run
/// the Noise leaf, frame the wire, arm the shell-side resend, track
/// `pending_outbound`, and persist the connection. Returns `Err` on
/// index-allocation or Noise failure (cleaning the partial leg, including
/// the dial-time control machine), leaving the armed wire on the connection
/// for `send_stored_msg1` to transmit. Does NOT send — so the fallible setup
/// can propagate its error synchronously before any machine drive. The
/// control machine itself is persisted at dial in `initiate_connection`;
/// this function no longer touches `peer_machines` except to clean it up on
/// the failure paths.
/// `pending_outbound`, and embed the prepared connection on the dial-time
/// control machine. Returns `Err` on index-allocation or Noise failure
/// (cleaning the partial leg, including the dial-time control machine),
/// leaving the armed wire on the connection for `send_stored_msg1` to
/// transmit. Does NOT send — so the fallible setup can propagate its error
/// synchronously before any machine drive. The control machine itself is
/// persisted at dial in `initiate_connection`; this function hands it the
/// connection on success and cleans it up on the failure paths.
pub(in crate::node) fn prepare_outbound_msg1(
&mut self,
link_id: LinkId,
@@ -633,7 +633,19 @@ impl Node {
// Track in pending_outbound for msg2 dispatch
self.pending_outbound
.insert((transport_id, our_index.as_u32()), link_id);
self.connections.insert(link_id, connection);
// The dial-born machine (persisted in `initiate_connection`) carries
// the prepared connection from here. Every live caller dialed first,
// so the machine exists; recover with a fresh one if a direct caller
// ever skips the dial.
debug_assert!(
self.peer_machines.contains_key(&link_id),
"outbound msg1 prepared for link {link_id} with no dial-time machine"
);
self.peer_machines
.entry(link_id)
.or_insert_with(|| PeerMachine::new_outbound(link_id, peer_identity, current_time_ms))
.set_leg(connection);
Ok(())
}
@@ -650,15 +662,11 @@ impl Node {
transport_id: TransportId,
remote_addr: &TransportAddr,
) {
let wire_msg1 = match self
.connections
.get(&link_id)
.and_then(|c| c.handshake_msg1())
{
let wire_msg1 = match self.leg(&link_id).and_then(|c| c.handshake_msg1()) {
Some(w) => w.to_vec(),
None => return,
};
let our_index = self.connections.get(&link_id).and_then(|c| c.our_index());
let our_index = self.leg(&link_id).and_then(|c| c.our_index());
// Send the wire format handshake message
if let Some(transport) = self.transports.get(&transport_id) {
@@ -681,7 +689,7 @@ impl Node {
);
// Mark connection as failed but don't remove it yet
// The event loop can handle retry logic
if let Some(conn) = self.connections.get_mut(&link_id) {
if let Some(conn) = self.leg_mut(&link_id) {
conn.mark_failed();
}
}
@@ -881,14 +889,13 @@ impl Node {
);
let now_ms = Self::now_ms();
let stale: Vec<LinkId> = self
.connections
.iter()
.filter(|(_, conn)| {
.connections()
.filter(|conn| {
conn.expected_identity()
.map(|id| id.node_addr() == &peer_addr)
.unwrap_or(false)
})
.map(|(link_id, _)| *link_id)
.map(|conn| conn.link_id())
.collect();
for link_id in stale {
self.cleanup_stale_connection(link_id, now_ms);
@@ -1821,7 +1828,7 @@ impl Node {
info!("Node started:");
info!(" state: {}", self.supervisor.state);
info!(" transports: {}", self.transports.len());
info!(" connections: {}", self.connections.len());
info!(" connections: {}", self.connection_count());
Ok(())
}
@@ -2665,18 +2672,17 @@ impl Node {
/// The `connected` / `connecting` sets gate the floor, retry-dial, overlay,
/// and LAN layers; `in_flight_by_peer` feeds the opportunistic layer's
/// per-peer parallel cap, computed exactly as the deleted
/// `path_candidate_attempt_budget` did: `connections(expected == addr) +
/// `path_candidate_attempt_budget` did: `pending legs(expected == addr) +
/// pending_connects(addr)`. The scalar counts stay unpopulated at the
/// ceiling-only posture (no layer reads them; see [`Observed`]).
pub(in crate::node) fn observe_peering(&self) -> Observed {
let connected: HashSet<NodeAddr> = self.peers.keys().copied().collect();
let connecting: HashSet<NodeAddr> = self
.connections
.values()
.connections()
.filter_map(|conn| conn.expected_identity().map(|id| *id.node_addr()))
.collect();
let mut in_flight_by_peer: HashMap<NodeAddr, usize> = HashMap::new();
for conn in self.connections.values() {
for conn in self.connections() {
if let Some(id) = conn.expected_identity() {
*in_flight_by_peer.entry(*id.node_addr()).or_default() += 1;
}
@@ -2715,9 +2721,11 @@ impl Node {
}
fn outbound_handshake_slots(&self) -> usize {
// Count pending connections (legs), not machines: a dial-born machine
// in the connect window has no connection yet but DOES hold a
// `pending_connects` slot, so counting machines would tally it twice.
let used = self
.connections
.len()
.connection_count()
.saturating_add(self.peering.pending_connects.len());
if self.max_connections() == 0 {
usize::MAX
@@ -2743,8 +2751,7 @@ impl Node {
}
let in_flight_for_peer = self
.connections
.values()
.connections()
.filter(|conn| {
conn.expected_identity()
.map(|identity| identity.node_addr() == peer_node_addr)