node: make the inbound handshake machine persistent across its leg's life

The inbound control machine was a msg3-time throwaway: handle_msg3 built
a transient, stepped it once for the decision, and discarded it, with
promote_connection birthing a fresh established() machine. Every
handshake leg now gets one persistent machine for its whole life:

- handle_msg1 births the machine parked in the sent-msg2 phase alongside
  the window leg (msg1 crypto and the msg2 build/send stay inline; the
  msg2-send-failure cleanup disposes it).
- handle_msg3 steps that persistent machine instead of a transient. The
  decision path is unchanged: the msg3 handler never reads machine state
  and overwrites the identity-plane fields from the wire outcome before
  dispatching, so every arm's decision and actions are byte-identical.
- promote_connection stops rebirthing: the executor feeds the promotion
  result back into the machine (the shape the IK line already uses) and
  the machine crystallizes to Established in place.
- Every path that tears down an inbound window leg now disposes its
  machine through remove_peer_machine: the seven inline msg3 termination
  arms, the six executor swap/rekey-responder consumption sites, both
  promote-failure arms, the single-peer leaf reject, the losing side of
  a cross-connection, and the msg1 send-failure cleanup. The
  promote-failure disposal also fixes a pre-existing leak: an outbound
  leg whose promotion fails had its connections entry consumed before
  the error, so the stale reaper could never reach its dial machine.

Index-free behavior is unchanged at every site. Two test helpers that
hand-roll outbound dials now insert the dial machine production
inserts, and new tests pin the machine's birth phase, its post-promote
crystallized state, and disposal on the failure and rekey-responder
arms.
This commit is contained in:
Johnathan Corgan
2026-07-17 02:54:00 +00:00
parent 3f9de1adcd
commit d6785f2970
5 changed files with 352 additions and 73 deletions
+7
View File
@@ -163,6 +163,13 @@ pub(super) async fn initiate_handshake(nodes: &mut [TestNode], i: usize, j: usiz
.addr_to_link
.insert((transport_id, responder_addr.clone()), link_id);
initiator.node.connections.insert(link_id, conn);
// Mirror the production dial path: an identified outbound leg persists
// its control machine at dial, and the promote feedback later
// crystallizes that same machine in place.
initiator.node.peer_machines.insert(
link_id,
crate::peer::machine::PeerMachine::new_outbound(link_id, peer_identity, 1000),
);
initiator
.node
.pending_outbound