Skip to main content

fips_core/peer/
connection.rs

1//! Peer Connection (Handshake Phase)
2//!
3//! Represents an in-progress connection before authentication completes.
4//! PeerConnection tracks the Noise IK handshake state and transitions to
5//! ActivePeer upon successful authentication.
6
7use crate::PeerIdentity;
8use crate::noise::{self, NoiseError, NoiseSession};
9use crate::transport::{LinkDirection, LinkId, LinkStats, TransportAddr, TransportId};
10use crate::utils::index::SessionIndex;
11use secp256k1::Keypair;
12use std::fmt;
13
14/// Handshake protocol state machine.
15///
16/// For Noise IK pattern:
17/// - Initiator: Initial → SentMsg1 → Complete
18/// - Responder: Initial → ReceivedMsg1 → Complete
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum HandshakeState {
21    /// Initial state, ready to start handshake.
22    Initial,
23    /// Initiator: Sent message 1, awaiting message 2.
24    SentMsg1,
25    /// Responder: Received message 1, ready to send message 2.
26    ReceivedMsg1,
27    /// Handshake completed successfully.
28    Complete,
29    /// Handshake failed.
30    Failed,
31}
32
33impl HandshakeState {
34    /// Check if handshake is still in progress.
35    pub fn is_in_progress(&self) -> bool {
36        matches!(
37            self,
38            HandshakeState::Initial | HandshakeState::SentMsg1 | HandshakeState::ReceivedMsg1
39        )
40    }
41
42    /// Check if handshake completed successfully.
43    pub fn is_complete(&self) -> bool {
44        matches!(self, HandshakeState::Complete)
45    }
46
47    /// Check if handshake failed.
48    pub fn is_failed(&self) -> bool {
49        matches!(self, HandshakeState::Failed)
50    }
51}
52
53impl fmt::Display for HandshakeState {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        let s = match self {
56            HandshakeState::Initial => "initial",
57            HandshakeState::SentMsg1 => "sent_msg1",
58            HandshakeState::ReceivedMsg1 => "received_msg1",
59            HandshakeState::Complete => "complete",
60            HandshakeState::Failed => "failed",
61        };
62        write!(f, "{}", s)
63    }
64}
65
66/// A connection in the handshake phase, before authentication completes.
67///
68/// For outbound connections, we know the expected peer identity from config.
69/// For inbound connections, we learn the identity during the Noise handshake.
70pub struct PeerConnection {
71    // === Link Reference ===
72    /// The link carrying this connection.
73    link_id: LinkId,
74
75    /// Connection direction (we initiated or they initiated).
76    direction: LinkDirection,
77
78    // === Handshake State ===
79    /// Current handshake state.
80    handshake_state: HandshakeState,
81
82    /// Expected peer identity (known for outbound, learned for inbound).
83    /// Updated after receiving their static key in the handshake.
84    expected_identity: Option<PeerIdentity>,
85
86    /// Noise handshake state (consumes on completion).
87    noise_handshake: Option<noise::HandshakeState>,
88
89    /// Completed Noise session (available after handshake complete).
90    noise_session: Option<NoiseSession>,
91
92    // === Timing ===
93    /// When the connection attempt started (Unix milliseconds).
94    started_at: u64,
95
96    /// When the last handshake message was sent/received.
97    last_activity: u64,
98
99    // === Statistics ===
100    /// Link statistics during handshake.
101    link_stats: LinkStats,
102
103    // === Wire Protocol Index Tracking ===
104    /// Our sender_idx for this handshake (chosen by us).
105    /// For outbound: included in msg1, used as receiver_idx in msg2 echo.
106    /// For inbound: chosen after processing msg1, included in msg2.
107    our_index: Option<SessionIndex>,
108
109    /// Their sender_idx (learned from their messages).
110    /// For outbound: learned from msg2.
111    /// For inbound: learned from msg1.
112    their_index: Option<SessionIndex>,
113
114    /// Transport ID (for index namespace).
115    transport_id: Option<TransportId>,
116
117    /// Current source address (updated on packet receipt).
118    source_addr: Option<TransportAddr>,
119
120    /// Preferred outbound send address learned during handshake.
121    preferred_send_addr: Option<TransportAddr>,
122
123    // === Epoch (Restart Detection) ===
124    /// Remote peer's startup epoch (learned from handshake).
125    remote_epoch: Option<[u8; 8]>,
126
127    // === Handshake Resend ===
128    /// Wire-format msg1 bytes for resend (initiator only).
129    handshake_msg1: Option<Vec<u8>>,
130
131    /// Wire-format msg2 bytes for resend (responder only).
132    handshake_msg2: Option<Vec<u8>>,
133
134    /// Number of resends performed so far.
135    resend_count: u32,
136
137    /// When the next resend should fire (Unix ms). 0 = no resend scheduled.
138    next_resend_at_ms: u64,
139}
140
141impl PeerConnection {
142    /// Create a new outbound connection (we are initiating).
143    ///
144    /// For outbound, we know who we're trying to reach from configuration.
145    /// The Noise handshake will be initialized when `start_handshake` is called.
146    pub fn outbound(
147        link_id: LinkId,
148        expected_identity: PeerIdentity,
149        current_time_ms: u64,
150    ) -> Self {
151        Self {
152            link_id,
153            direction: LinkDirection::Outbound,
154            handshake_state: HandshakeState::Initial,
155            expected_identity: Some(expected_identity),
156            noise_handshake: None,
157            noise_session: None,
158            started_at: current_time_ms,
159            last_activity: current_time_ms,
160
161            link_stats: LinkStats::new(),
162            our_index: None,
163            their_index: None,
164            transport_id: None,
165            source_addr: None,
166            preferred_send_addr: None,
167            remote_epoch: None,
168            handshake_msg1: None,
169            handshake_msg2: None,
170            resend_count: 0,
171            next_resend_at_ms: 0,
172        }
173    }
174
175    /// Create a new inbound connection (they are initiating).
176    ///
177    /// For inbound, we don't know who they are until we decrypt their
178    /// identity from Noise message 1.
179    pub fn inbound(link_id: LinkId, current_time_ms: u64) -> Self {
180        Self {
181            link_id,
182            direction: LinkDirection::Inbound,
183            handshake_state: HandshakeState::Initial,
184            expected_identity: None,
185            noise_handshake: None,
186            noise_session: None,
187            started_at: current_time_ms,
188            last_activity: current_time_ms,
189
190            link_stats: LinkStats::new(),
191            our_index: None,
192            their_index: None,
193            transport_id: None,
194            source_addr: None,
195            preferred_send_addr: None,
196            remote_epoch: None,
197            handshake_msg1: None,
198            handshake_msg2: None,
199            resend_count: 0,
200            next_resend_at_ms: 0,
201        }
202    }
203
204    /// Create a new inbound connection with transport information.
205    ///
206    /// Used when processing msg1 where we know the transport and source address.
207    pub fn inbound_with_transport(
208        link_id: LinkId,
209        transport_id: TransportId,
210        source_addr: TransportAddr,
211        current_time_ms: u64,
212    ) -> Self {
213        Self {
214            link_id,
215            direction: LinkDirection::Inbound,
216            handshake_state: HandshakeState::Initial,
217            expected_identity: None,
218            noise_handshake: None,
219            noise_session: None,
220            started_at: current_time_ms,
221            last_activity: current_time_ms,
222
223            link_stats: LinkStats::new(),
224            our_index: None,
225            their_index: None,
226            transport_id: Some(transport_id),
227            source_addr: Some(source_addr),
228            preferred_send_addr: None,
229            remote_epoch: None,
230            handshake_msg1: None,
231            handshake_msg2: None,
232            resend_count: 0,
233            next_resend_at_ms: 0,
234        }
235    }
236
237    // === Accessors ===
238
239    /// Get the link ID.
240    pub fn link_id(&self) -> LinkId {
241        self.link_id
242    }
243
244    /// Get the connection direction.
245    pub fn direction(&self) -> LinkDirection {
246        self.direction
247    }
248
249    /// Get the handshake state.
250    pub fn handshake_state(&self) -> HandshakeState {
251        self.handshake_state
252    }
253
254    /// Get the expected/learned peer identity, if known.
255    pub fn expected_identity(&self) -> Option<&PeerIdentity> {
256        self.expected_identity.as_ref()
257    }
258
259    /// Check if this is an outbound connection.
260    pub fn is_outbound(&self) -> bool {
261        self.direction == LinkDirection::Outbound
262    }
263
264    /// Check if this is an inbound connection.
265    pub fn is_inbound(&self) -> bool {
266        self.direction == LinkDirection::Inbound
267    }
268
269    /// Check if handshake is in progress.
270    pub fn is_in_progress(&self) -> bool {
271        self.handshake_state.is_in_progress()
272    }
273
274    /// Check if handshake completed.
275    pub fn is_complete(&self) -> bool {
276        self.handshake_state.is_complete()
277    }
278
279    /// Check if handshake failed.
280    pub fn is_failed(&self) -> bool {
281        self.handshake_state.is_failed()
282    }
283
284    /// When the connection started.
285    pub fn started_at(&self) -> u64 {
286        self.started_at
287    }
288
289    /// When the last activity occurred.
290    pub fn last_activity(&self) -> u64 {
291        self.last_activity
292    }
293
294    /// Connection duration so far.
295    pub fn duration(&self, current_time_ms: u64) -> u64 {
296        current_time_ms.saturating_sub(self.started_at)
297    }
298
299    /// Time since last activity.
300    pub fn idle_time(&self, current_time_ms: u64) -> u64 {
301        current_time_ms.saturating_sub(self.last_activity)
302    }
303
304    /// Get link statistics.
305    pub fn link_stats(&self) -> &LinkStats {
306        &self.link_stats
307    }
308
309    /// Get mutable link statistics.
310    pub fn link_stats_mut(&mut self) -> &mut LinkStats {
311        &mut self.link_stats
312    }
313
314    // === Index Accessors ===
315
316    /// Get our session index (if set).
317    pub fn our_index(&self) -> Option<SessionIndex> {
318        self.our_index
319    }
320
321    /// Set our session index.
322    pub fn set_our_index(&mut self, index: SessionIndex) {
323        self.our_index = Some(index);
324    }
325
326    /// Get their session index (if known).
327    pub fn their_index(&self) -> Option<SessionIndex> {
328        self.their_index
329    }
330
331    /// Set their session index.
332    pub fn set_their_index(&mut self, index: SessionIndex) {
333        self.their_index = Some(index);
334    }
335
336    /// Get the transport ID (if set).
337    pub fn transport_id(&self) -> Option<TransportId> {
338        self.transport_id
339    }
340
341    /// Set the transport ID.
342    pub fn set_transport_id(&mut self, id: TransportId) {
343        self.transport_id = Some(id);
344    }
345
346    /// Get the source address (if known).
347    pub fn source_addr(&self) -> Option<&TransportAddr> {
348        self.source_addr.as_ref()
349    }
350
351    /// Set the source address.
352    pub fn set_source_addr(&mut self, addr: TransportAddr) {
353        self.source_addr = Some(addr);
354    }
355
356    /// Get the preferred outbound send address, if one was learned.
357    pub fn preferred_send_addr(&self) -> Option<&TransportAddr> {
358        self.preferred_send_addr.as_ref()
359    }
360
361    /// Set the preferred outbound send address.
362    pub fn set_preferred_send_addr(&mut self, addr: TransportAddr) {
363        self.preferred_send_addr = Some(addr);
364    }
365
366    // === Epoch Accessors ===
367
368    /// Get the remote peer's startup epoch (available after handshake).
369    pub fn remote_epoch(&self) -> Option<[u8; 8]> {
370        self.remote_epoch
371    }
372
373    // === Handshake Resend ===
374
375    /// Store the wire-format msg1 bytes for resend and schedule the first resend.
376    pub fn set_handshake_msg1(&mut self, msg1: Vec<u8>, first_resend_at_ms: u64) {
377        self.handshake_msg1 = Some(msg1);
378        self.resend_count = 0;
379        self.next_resend_at_ms = first_resend_at_ms;
380    }
381
382    /// Store the wire-format msg2 bytes for resend on duplicate msg1.
383    pub fn set_handshake_msg2(&mut self, msg2: Vec<u8>) {
384        self.handshake_msg2 = Some(msg2);
385    }
386
387    /// Get the stored msg1 bytes (if any).
388    pub fn handshake_msg1(&self) -> Option<&[u8]> {
389        self.handshake_msg1.as_deref()
390    }
391
392    /// Get the stored msg2 bytes (if any).
393    pub fn handshake_msg2(&self) -> Option<&[u8]> {
394        self.handshake_msg2.as_deref()
395    }
396
397    /// Number of resends performed.
398    pub fn resend_count(&self) -> u32 {
399        self.resend_count
400    }
401
402    /// When the next resend is scheduled (Unix ms).
403    pub fn next_resend_at_ms(&self) -> u64 {
404        self.next_resend_at_ms
405    }
406
407    /// Make a scheduled outbound Msg1 retry immediately eligible.
408    ///
409    /// A positive local network-change event invalidates the route assumptions
410    /// behind the existing deadline and resend history. This preserves the
411    /// Noise state while granting the same candidate a fresh bounded resend
412    /// budget on the rebound carrier.
413    pub(crate) fn restart_handshake_resends(&mut self, now_ms: u64) {
414        if self.next_resend_at_ms > 0 {
415            self.resend_count = 0;
416            self.next_resend_at_ms = now_ms;
417        }
418    }
419
420    /// Record a resend and schedule the next one.
421    pub fn record_resend(&mut self, next_resend_at_ms: u64) {
422        self.resend_count += 1;
423        self.next_resend_at_ms = next_resend_at_ms;
424    }
425
426    // === Noise Handshake Operations ===
427
428    /// Start the handshake as initiator and generate message 1.
429    ///
430    /// For outbound connections only. Returns the handshake message to send.
431    /// The epoch is our startup epoch, encrypted into msg1 for restart detection.
432    pub fn start_handshake(
433        &mut self,
434        our_keypair: Keypair,
435        epoch: [u8; 8],
436        current_time_ms: u64,
437    ) -> Result<Vec<u8>, NoiseError> {
438        if self.direction != LinkDirection::Outbound {
439            return Err(NoiseError::WrongState {
440                expected: "outbound connection".to_string(),
441                got: "inbound connection".to_string(),
442            });
443        }
444
445        if self.handshake_state != HandshakeState::Initial {
446            return Err(NoiseError::WrongState {
447                expected: "initial state".to_string(),
448                got: self.handshake_state.to_string(),
449            });
450        }
451
452        let remote_static = self
453            .expected_identity
454            .as_ref()
455            .expect("outbound must have expected identity")
456            .pubkey_full();
457
458        let mut hs = noise::HandshakeState::new_initiator(our_keypair, remote_static);
459        hs.set_local_epoch(epoch);
460        let msg1 = hs.write_message_1()?;
461
462        self.noise_handshake = Some(hs);
463        self.handshake_state = HandshakeState::SentMsg1;
464        self.last_activity = current_time_ms;
465
466        Ok(msg1)
467    }
468
469    /// Initialize responder and process incoming message 1.
470    ///
471    /// For inbound connections only. Returns the handshake message 2 to send.
472    /// The epoch is our startup epoch, encrypted into msg2 for restart detection.
473    pub fn receive_handshake_init(
474        &mut self,
475        our_keypair: Keypair,
476        epoch: [u8; 8],
477        message: &[u8],
478        current_time_ms: u64,
479    ) -> Result<Vec<u8>, NoiseError> {
480        if self.direction != LinkDirection::Inbound {
481            return Err(NoiseError::WrongState {
482                expected: "inbound connection".to_string(),
483                got: "outbound connection".to_string(),
484            });
485        }
486
487        if self.handshake_state != HandshakeState::Initial {
488            return Err(NoiseError::WrongState {
489                expected: "initial state".to_string(),
490                got: self.handshake_state.to_string(),
491            });
492        }
493
494        let mut hs = noise::HandshakeState::new_responder(our_keypair);
495        hs.set_local_epoch(epoch);
496
497        // Process message 1 (this reveals the initiator's identity and epoch)
498        hs.read_message_1(message)?;
499
500        // Extract the discovered identity
501        let remote_static = *hs
502            .remote_static()
503            .expect("remote static available after msg1");
504        self.expected_identity = Some(PeerIdentity::from_pubkey_full(remote_static));
505
506        // Capture remote epoch from msg1
507        self.remote_epoch = hs.remote_epoch();
508
509        // Generate message 2
510        let msg2 = hs.write_message_2()?;
511
512        // Handshake is complete for responder
513        let session = hs.into_session()?;
514        self.noise_session = Some(session);
515        self.handshake_state = HandshakeState::Complete;
516        self.last_activity = current_time_ms;
517
518        Ok(msg2)
519    }
520
521    /// Complete the handshake by processing message 2.
522    ///
523    /// For outbound connections only (initiator completing handshake).
524    pub fn complete_handshake(
525        &mut self,
526        message: &[u8],
527        current_time_ms: u64,
528    ) -> Result<(), NoiseError> {
529        if self.handshake_state != HandshakeState::SentMsg1 {
530            return Err(NoiseError::WrongState {
531                expected: "sent_msg1 state".to_string(),
532                got: self.handshake_state.to_string(),
533            });
534        }
535
536        let mut hs = self
537            .noise_handshake
538            .take()
539            .expect("noise handshake must exist in SentMsg1 state");
540
541        hs.read_message_2(message)?;
542
543        // Capture remote epoch from msg2
544        self.remote_epoch = hs.remote_epoch();
545
546        let session = hs.into_session()?;
547        self.noise_session = Some(session);
548        self.handshake_state = HandshakeState::Complete;
549        self.last_activity = current_time_ms;
550
551        Ok(())
552    }
553
554    /// Take the completed Noise session.
555    ///
556    /// Returns the NoiseSession for use in ActivePeer. Can only be called
557    /// once after handshake completes.
558    pub fn take_session(&mut self) -> Option<NoiseSession> {
559        if self.handshake_state == HandshakeState::Complete {
560            self.noise_session.take()
561        } else {
562            None
563        }
564    }
565
566    /// Check if we have a completed session ready to take.
567    pub fn has_session(&self) -> bool {
568        self.handshake_state == HandshakeState::Complete && self.noise_session.is_some()
569    }
570
571    // === State Transitions (for manual control if needed) ===
572
573    /// Mark handshake as failed.
574    pub fn mark_failed(&mut self) {
575        self.handshake_state = HandshakeState::Failed;
576        self.noise_handshake = None;
577    }
578
579    /// Update last activity timestamp.
580    pub fn touch(&mut self, current_time_ms: u64) {
581        self.last_activity = current_time_ms;
582    }
583
584    // === Validation ===
585
586    /// Check if the connection has timed out.
587    pub fn is_timed_out(&self, current_time_ms: u64, timeout_ms: u64) -> bool {
588        self.idle_time(current_time_ms) > timeout_ms
589    }
590}
591
592impl fmt::Debug for PeerConnection {
593    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
594        f.debug_struct("PeerConnection")
595            .field("link_id", &self.link_id)
596            .field("direction", &self.direction)
597            .field("handshake_state", &self.handshake_state)
598            .field("expected_identity", &self.expected_identity)
599            .field("has_noise_handshake", &self.noise_handshake.is_some())
600            .field("has_noise_session", &self.noise_session.is_some())
601            .field("our_index", &self.our_index)
602            .field("their_index", &self.their_index)
603            .field("transport_id", &self.transport_id)
604            .field("started_at", &self.started_at)
605            .field("last_activity", &self.last_activity)
606            .finish()
607    }
608}
609
610#[cfg(test)]
611mod tests {
612    use super::*;
613    use crate::Identity;
614    use rand::Rng;
615
616    fn make_peer_identity() -> PeerIdentity {
617        let identity = Identity::generate();
618        PeerIdentity::from_pubkey(identity.pubkey())
619    }
620
621    fn make_keypair() -> Keypair {
622        let identity = Identity::generate();
623        identity.keypair()
624    }
625
626    fn make_epoch() -> [u8; 8] {
627        let mut epoch = [0u8; 8];
628        rand::rng().fill_bytes(&mut epoch);
629        epoch
630    }
631
632    #[test]
633    fn test_handshake_state_properties() {
634        assert!(HandshakeState::Initial.is_in_progress());
635        assert!(HandshakeState::SentMsg1.is_in_progress());
636        assert!(HandshakeState::ReceivedMsg1.is_in_progress());
637        assert!(!HandshakeState::Complete.is_in_progress());
638        assert!(!HandshakeState::Failed.is_in_progress());
639
640        assert!(HandshakeState::Complete.is_complete());
641        assert!(HandshakeState::Failed.is_failed());
642    }
643
644    #[test]
645    fn test_outbound_connection() {
646        let identity = make_peer_identity();
647        let conn = PeerConnection::outbound(LinkId::new(1), identity, 1000);
648
649        assert!(conn.is_outbound());
650        assert!(!conn.is_inbound());
651        assert_eq!(conn.handshake_state(), HandshakeState::Initial);
652        assert!(conn.expected_identity().is_some());
653        assert_eq!(conn.started_at(), 1000);
654    }
655
656    #[test]
657    fn test_inbound_connection() {
658        let conn = PeerConnection::inbound(LinkId::new(2), 2000);
659
660        assert!(conn.is_inbound());
661        assert!(!conn.is_outbound());
662        assert_eq!(conn.handshake_state(), HandshakeState::Initial);
663        assert!(conn.expected_identity().is_none());
664        assert_eq!(conn.started_at(), 2000);
665    }
666
667    #[test]
668    fn test_full_handshake_flow() {
669        // Create identities
670        let initiator_identity = Identity::generate();
671        let responder_identity = Identity::generate();
672
673        let initiator_keypair = initiator_identity.keypair();
674        let responder_keypair = responder_identity.keypair();
675        let initiator_epoch = make_epoch();
676        let responder_epoch = make_epoch();
677
678        // Use from_pubkey_full to preserve parity for ECDH
679        let responder_peer_id = PeerIdentity::from_pubkey_full(responder_identity.pubkey_full());
680
681        // Create connections
682        let mut initiator_conn = PeerConnection::outbound(LinkId::new(1), responder_peer_id, 1000);
683        let mut responder_conn = PeerConnection::inbound(LinkId::new(2), 1000);
684
685        // Initiator starts handshake
686        let msg1 = initiator_conn
687            .start_handshake(initiator_keypair, initiator_epoch, 1100)
688            .unwrap();
689        assert_eq!(initiator_conn.handshake_state(), HandshakeState::SentMsg1);
690
691        // Responder processes msg1 and sends msg2
692        let msg2 = responder_conn
693            .receive_handshake_init(responder_keypair, responder_epoch, &msg1, 1200)
694            .unwrap();
695        assert_eq!(responder_conn.handshake_state(), HandshakeState::Complete);
696
697        // Responder learned initiator's identity
698        let discovered = responder_conn.expected_identity().unwrap();
699        assert_eq!(discovered.pubkey(), initiator_identity.pubkey());
700
701        // Responder learned initiator's epoch
702        assert_eq!(responder_conn.remote_epoch(), Some(initiator_epoch));
703
704        // Initiator completes handshake
705        initiator_conn.complete_handshake(&msg2, 1300).unwrap();
706        assert_eq!(initiator_conn.handshake_state(), HandshakeState::Complete);
707
708        // Initiator learned responder's epoch
709        assert_eq!(initiator_conn.remote_epoch(), Some(responder_epoch));
710
711        // Both have sessions
712        assert!(initiator_conn.has_session());
713        assert!(responder_conn.has_session());
714
715        // Take and verify sessions work
716        let mut init_session = initiator_conn.take_session().unwrap();
717        let mut resp_session = responder_conn.take_session().unwrap();
718
719        // Encrypt/decrypt test
720        let plaintext = b"test message";
721        let ciphertext = init_session.encrypt(plaintext).unwrap();
722        let decrypted = resp_session.decrypt(&ciphertext).unwrap();
723        assert_eq!(decrypted, plaintext);
724    }
725
726    #[test]
727    fn test_connection_timing() {
728        let identity = make_peer_identity();
729        let conn = PeerConnection::outbound(LinkId::new(1), identity, 1000);
730
731        assert_eq!(conn.duration(1500), 500);
732        assert_eq!(conn.idle_time(1500), 500);
733        assert!(!conn.is_timed_out(1500, 1000));
734        assert!(conn.is_timed_out(2500, 1000));
735    }
736
737    #[test]
738    fn test_connection_failure() {
739        let identity = make_peer_identity();
740        let mut conn = PeerConnection::outbound(LinkId::new(1), identity, 1000);
741
742        conn.mark_failed();
743        assert!(conn.is_failed());
744        assert!(!conn.is_in_progress());
745        assert!(!conn.is_complete());
746    }
747
748    #[test]
749    fn test_wrong_direction_errors() {
750        let identity = make_peer_identity();
751        let keypair = make_keypair();
752
753        // Outbound can't receive_handshake_init
754        let mut outbound = PeerConnection::outbound(LinkId::new(1), identity, 1000);
755        assert!(
756            outbound
757                .receive_handshake_init(keypair, make_epoch(), &[0u8; 106], 1100)
758                .is_err()
759        );
760
761        // Inbound can't start_handshake
762        let mut inbound = PeerConnection::inbound(LinkId::new(2), 1000);
763        assert!(
764            inbound
765                .start_handshake(keypair, make_epoch(), 1100)
766                .is_err()
767        );
768    }
769}