peer: drive the connection-oriented outbound dial through the machine

Route the connection-oriented (TCP/Tor) outbound path through the peer state
machine, matching how the connectionless path already works.
initiate_connection's oriented branch now drives PeerEvent::Dial with
connection_oriented=true; the machine parks in Connecting and emits
OpenTransport, whose executor arm performs the non-blocking transport.connect
and pushes the PendingConnect. When the connect resolves, poll_pending_connects
prepares msg1 in the shell and then drives PeerEvent::TransportConnected, which
sends msg1 via the machine's SendHandshake arm.

The msg1 prepare (index allocation, Noise leaf, wire arming) MUST run in the
shell before the TransportConnected drive: send_stored_msg1 only transmits an
already-armed wire, so a drive-only path would silently send nothing. The
machine's our_index stays unset; the connect-failure path keeps its direct
handshake-timeout handling (TransportFailed stays dormant). The now-unused
Node::start_handshake helper is removed.

Behavior-neutral: same transport.connect, same PendingConnect, same msg1 send
and failure teardown as the removed inline path -- only the driver changes from
inline shell code to the state machine.
This commit is contained in:
Johnathan Corgan
2026-07-15 14:40:58 +00:00
parent 9588c50063
commit 3e7ca90212
3 changed files with 119 additions and 74 deletions
+4 -4
View File
@@ -1141,10 +1141,10 @@ impl Node {
// net-new arm — no ordering constraint, lowest risk.
//
// Look up the outbound machine persisted at DIAL, and step `Msg2 →
// [PromoteToActive]` in place. It is in `Discovered` (connection-oriented
// dial, not yet cut over) or `Handshaking{SentMsg1}` (connectionless dial,
// which drives the machine to send msg1) — either way `on_msg2` is
// state-independent: it decides from the outbound snapshot, not the
// [PromoteToActive]` in place. Both dial paths reach msg2 in
// `Handshaking{SentMsg1}` — each drives the machine to send msg1 before
// msg2 — and `on_msg2` is state-independent regardless: it decides from
// the outbound snapshot, not the
// machine's state, and reads the machine's unset `conn.our_index`
// as `None`, so stepping the persisted (vs the former transient) machine
// is byte-identical: same `Promote` decision (`has_existing_peer == false`),