Skip to main content

rns_core/channel/
mod.rs

1pub mod envelope;
2pub mod types;
3
4use alloc::collections::VecDeque;
5use alloc::vec::Vec;
6
7#[cfg(test)]
8use crate::constants::CHANNEL_SEQ_MAX;
9use crate::constants::{
10    CHANNEL_ENVELOPE_OVERHEAD, CHANNEL_FAST_RATE_THRESHOLD, CHANNEL_MAX_TRIES, CHANNEL_RTT_FAST,
11    CHANNEL_RTT_MEDIUM, CHANNEL_RTT_SLOW, CHANNEL_SEQ_MODULUS, CHANNEL_WINDOW,
12    CHANNEL_WINDOW_FLEXIBILITY, CHANNEL_WINDOW_MAX_FAST, CHANNEL_WINDOW_MAX_MEDIUM,
13    CHANNEL_WINDOW_MAX_SLOW, CHANNEL_WINDOW_MIN, CHANNEL_WINDOW_MIN_LIMIT_FAST,
14    CHANNEL_WINDOW_MIN_LIMIT_MEDIUM,
15};
16
17pub use types::{ChannelAction, ChannelError, MessageType, Sequence};
18
19use envelope::{pack_envelope, unpack_envelope};
20
21/// Internal envelope tracking state.
22struct Envelope {
23    sequence: Sequence,
24    raw: Vec<u8>,
25    tries: u8,
26    sent_at: f64,
27    delivered: bool,
28}
29
30/// Window-based reliable messaging channel.
31///
32/// Follows the action-queue model: `send`/`receive`/`tick` return
33/// `Vec<ChannelAction>`. The caller dispatches actions.
34pub struct Channel {
35    tx_ring: VecDeque<Envelope>,
36    rx_ring: VecDeque<Envelope>,
37    next_sequence: u16,
38    next_rx_sequence: u16,
39    window: u16,
40    window_max: u16,
41    window_min: u16,
42    window_flexibility: u16,
43    fast_rate_rounds: u16,
44    medium_rate_rounds: u16,
45    max_tries: u8,
46    rtt: f64,
47}
48
49impl Channel {
50    /// Create a new Channel with initial RTT.
51    pub fn new(initial_rtt: f64) -> Self {
52        let (window, window_max, window_min, window_flexibility) = if initial_rtt > CHANNEL_RTT_SLOW
53        {
54            (1, 1, 1, 1)
55        } else {
56            (
57                CHANNEL_WINDOW,
58                CHANNEL_WINDOW_MAX_SLOW,
59                CHANNEL_WINDOW_MIN,
60                CHANNEL_WINDOW_FLEXIBILITY,
61            )
62        };
63
64        Channel {
65            tx_ring: VecDeque::new(),
66            rx_ring: VecDeque::new(),
67            next_sequence: 0,
68            next_rx_sequence: 0,
69            window,
70            window_max,
71            window_min,
72            window_flexibility,
73            fast_rate_rounds: 0,
74            medium_rate_rounds: 0,
75            max_tries: CHANNEL_MAX_TRIES,
76            rtt: initial_rtt,
77        }
78    }
79
80    /// Update the RTT value.
81    pub fn set_rtt(&mut self, rtt: f64) {
82        self.rtt = rtt;
83    }
84
85    /// Maximum data unit available for message payload.
86    pub fn mdu(&self, link_mdu: usize) -> usize {
87        let mdu = link_mdu.saturating_sub(CHANNEL_ENVELOPE_OVERHEAD);
88        mdu.min(0xFFFF)
89    }
90
91    /// Check if channel is ready to send (has window capacity).
92    pub fn is_ready_to_send(&self) -> bool {
93        let outstanding = self.tx_ring.iter().filter(|e| !e.delivered).count() as u16;
94        outstanding < self.window
95    }
96
97    /// Send a message. Returns `SendOnLink` action with packed envelope.
98    pub fn send(
99        &mut self,
100        msgtype: u16,
101        payload: &[u8],
102        now: f64,
103        link_mdu: usize,
104    ) -> Result<Vec<ChannelAction>, ChannelError> {
105        if !self.is_ready_to_send() {
106            return Err(ChannelError::NotReady);
107        }
108
109        let sequence = self.next_sequence;
110        let raw = pack_envelope(msgtype, sequence, payload);
111        if raw.len() > link_mdu {
112            return Err(ChannelError::MessageTooBig);
113        }
114
115        self.next_sequence = ((self.next_sequence as u32 + 1) % CHANNEL_SEQ_MODULUS) as u16;
116        self.tx_ring.push_back(Envelope {
117            sequence,
118            raw: raw.clone(),
119            tries: 1,
120            sent_at: now,
121            delivered: false,
122        });
123
124        Ok(alloc::vec![ChannelAction::SendOnLink { raw, sequence }])
125    }
126
127    /// Receive decrypted envelope bytes.
128    ///
129    /// Returns `MessageReceived` for contiguous sequences starting from
130    /// `next_rx_sequence`.
131    pub fn receive(&mut self, raw: &[u8], _now: f64) -> Vec<ChannelAction> {
132        let (_msgtype, sequence, _payload) = match unpack_envelope(raw) {
133            Ok(r) => r,
134            Err(_) => return Vec::new(),
135        };
136
137        // Reject sequences behind or more than one maximum receive window
138        // ahead. Wrapping subtraction gives the forward modular distance and
139        // handles the 0xffff -> 0 boundary without a separate branch.
140        if self.is_outside_rx_window(sequence) {
141            return Vec::new();
142        }
143
144        // Reject duplicates
145        if self.rx_ring.iter().any(|e| e.sequence == sequence) {
146            return Vec::new();
147        }
148
149        // Emplace in sorted order
150        let envelope = Envelope {
151            sequence,
152            raw: raw.to_vec(),
153            tries: 0,
154            sent_at: 0.0,
155            delivered: false,
156        };
157        self.emplace_rx(envelope);
158
159        // Collect contiguous messages
160        self.collect_contiguous()
161    }
162
163    /// Clear all outstanding TX entries, restoring the window to full capacity.
164    /// Used after holepunch completion where signaling messages are fire-and-forget.
165    pub fn flush_tx(&mut self) {
166        self.tx_ring.clear();
167    }
168
169    /// Cancel a send that did not reach the link layer.
170    pub fn cancel_send(&mut self, sequence: Sequence) -> bool {
171        let Some(pos) = self.tx_ring.iter().position(|e| e.sequence == sequence) else {
172            return false;
173        };
174        self.tx_ring.remove(pos);
175        let expected_next = ((sequence as u32 + 1) % CHANNEL_SEQ_MODULUS) as u16;
176        if self.next_sequence == expected_next {
177            self.next_sequence = sequence;
178        }
179        true
180    }
181
182    /// Notify that a packet with given sequence was delivered (acknowledged).
183    pub fn packet_delivered(&mut self, sequence: Sequence) -> Vec<ChannelAction> {
184        if let Some(pos) = self.tx_ring.iter().position(|e| e.sequence == sequence) {
185            self.tx_ring.remove(pos);
186
187            if self.window < self.window_max {
188                self.window += 1;
189            }
190
191            // Adapt window based on RTT
192            self.adapt_window_on_delivery();
193        }
194        Vec::new()
195    }
196
197    /// Notify that a packet with given sequence timed out.
198    pub fn packet_timeout(&mut self, sequence: Sequence, now: f64) -> Vec<ChannelAction> {
199        let pos = match self.tx_ring.iter().position(|e| e.sequence == sequence) {
200            Some(p) => p,
201            None => return Vec::new(),
202        };
203
204        let envelope = &self.tx_ring[pos];
205        if envelope.tries >= self.max_tries {
206            self.tx_ring.clear();
207            self.rx_ring.clear();
208            return alloc::vec![ChannelAction::TeardownLink];
209        }
210
211        // Retry
212        let envelope = &mut self.tx_ring[pos];
213        envelope.tries += 1;
214        envelope.sent_at = now;
215        let raw = envelope.raw.clone();
216
217        // Shrink window (Python nests window_max shrink inside window shrink)
218        if self.window > self.window_min {
219            self.window -= 1;
220            if self.window_max > self.window_min + self.window_flexibility {
221                self.window_max -= 1;
222            }
223        }
224
225        alloc::vec![ChannelAction::SendOnLink { raw, sequence }]
226    }
227
228    /// Compute timeout duration for the given try count.
229    ///
230    /// Formula: `1.5^(tries-1) * max(rtt*2.5, 0.025) * (tx_ring.len() + 1.5)`
231    pub fn get_packet_timeout(&self, tries: u8) -> f64 {
232        let base = 1.5_f64.powi((tries as i32) - 1);
233        let rtt_factor = (self.rtt * 2.5).max(0.025);
234        let ring_factor = (self.tx_ring.len() as f64) + 1.5;
235        base * rtt_factor * ring_factor
236    }
237
238    /// Get the current try count for a given sequence.
239    pub fn get_tries(&self, sequence: Sequence) -> Option<u8> {
240        self.tx_ring
241            .iter()
242            .find(|e| e.sequence == sequence)
243            .map(|e| e.tries)
244    }
245
246    /// Periodic maintenance for retransmissions and timeout handling.
247    pub fn tick(&mut self, now: f64) -> Vec<ChannelAction> {
248        let timed_out: Vec<Sequence> = self
249            .tx_ring
250            .iter()
251            .filter(|e| !e.delivered && now - e.sent_at >= self.get_packet_timeout(e.tries))
252            .map(|e| e.sequence)
253            .collect();
254
255        let mut actions = Vec::new();
256        for sequence in timed_out {
257            actions.extend(self.packet_timeout(sequence, now));
258        }
259        actions
260    }
261
262    /// Shut down the channel, clearing all rings.
263    pub fn shutdown(&mut self) {
264        self.tx_ring.clear();
265        self.rx_ring.clear();
266    }
267
268    /// Current window size.
269    pub fn window(&self) -> u16 {
270        self.window
271    }
272
273    /// Current maximum window size.
274    pub fn window_max(&self) -> u16 {
275        self.window_max
276    }
277
278    /// Number of outstanding (undelivered) envelopes in TX ring.
279    pub fn outstanding(&self) -> usize {
280        self.tx_ring.iter().filter(|e| !e.delivered).count()
281    }
282
283    // --- Internal ---
284
285    fn is_outside_rx_window(&self, sequence: Sequence) -> bool {
286        sequence.wrapping_sub(self.next_rx_sequence) > CHANNEL_WINDOW_MAX_FAST
287    }
288
289    fn emplace_rx(&mut self, envelope: Envelope) {
290        // Use modular distance from next_rx_sequence for correct wrap-boundary ordering.
291        // wrapping_sub gives the unsigned distance in sequence space.
292        let env_dist = envelope.sequence.wrapping_sub(self.next_rx_sequence);
293        for (i, existing) in self.rx_ring.iter().enumerate() {
294            if envelope.sequence == existing.sequence {
295                return; // duplicate
296            }
297            let exist_dist = existing.sequence.wrapping_sub(self.next_rx_sequence);
298            if env_dist < exist_dist {
299                self.rx_ring.insert(i, envelope);
300                return;
301            }
302        }
303        self.rx_ring.push_back(envelope);
304    }
305
306    fn collect_contiguous(&mut self) -> Vec<ChannelAction> {
307        let mut actions = Vec::new();
308
309        loop {
310            let front_match = self
311                .rx_ring
312                .front()
313                .map(|e| e.sequence == self.next_rx_sequence)
314                .unwrap_or(false);
315
316            if !front_match {
317                break;
318            }
319
320            let envelope = self.rx_ring.pop_front().unwrap();
321
322            // Re-parse the envelope to get payload
323            if let Ok((msgtype, _seq, payload)) = unpack_envelope(&envelope.raw) {
324                actions.push(ChannelAction::MessageReceived {
325                    msgtype,
326                    payload: payload.to_vec(),
327                    sequence: envelope.sequence,
328                });
329            }
330
331            self.next_rx_sequence =
332                ((self.next_rx_sequence as u32 + 1) % CHANNEL_SEQ_MODULUS) as u16;
333
334            // After wrapping to 0, check if 0 is also in the ring
335            if self.next_rx_sequence == 0 {
336                // Continue the loop — it will check front again
337            }
338        }
339
340        actions
341    }
342
343    fn adapt_window_on_delivery(&mut self) {
344        if self.rtt == 0.0 {
345            return;
346        }
347
348        if self.rtt > CHANNEL_RTT_FAST {
349            self.fast_rate_rounds = 0;
350
351            if self.rtt > CHANNEL_RTT_MEDIUM {
352                self.medium_rate_rounds = 0;
353            } else {
354                self.medium_rate_rounds += 1;
355                if self.window_max < CHANNEL_WINDOW_MAX_MEDIUM
356                    && self.medium_rate_rounds == CHANNEL_FAST_RATE_THRESHOLD
357                {
358                    self.window_max = CHANNEL_WINDOW_MAX_MEDIUM;
359                    self.window_min = CHANNEL_WINDOW_MIN_LIMIT_MEDIUM;
360                }
361            }
362        } else {
363            self.fast_rate_rounds += 1;
364            if self.window_max < CHANNEL_WINDOW_MAX_FAST
365                && self.fast_rate_rounds == CHANNEL_FAST_RATE_THRESHOLD
366            {
367                self.window_max = CHANNEL_WINDOW_MAX_FAST;
368                self.window_min = CHANNEL_WINDOW_MIN_LIMIT_FAST;
369            }
370        }
371    }
372}
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377
378    #[test]
379    fn test_new_default() {
380        let ch = Channel::new(0.5);
381        assert_eq!(ch.window, CHANNEL_WINDOW);
382        assert_eq!(ch.window_max, CHANNEL_WINDOW_MAX_SLOW);
383        assert!(ch.is_ready_to_send());
384    }
385
386    #[test]
387    fn test_new_very_slow() {
388        let ch = Channel::new(2.0);
389        assert_eq!(ch.window, 1);
390        assert_eq!(ch.window_max, 1);
391    }
392
393    #[test]
394    fn test_send_receive() {
395        let mut ch = Channel::new(0.1);
396        let actions = ch.send(0x01, b"hello", 1.0, 500).unwrap();
397        assert_eq!(actions.len(), 1);
398        match &actions[0] {
399            ChannelAction::SendOnLink { raw, sequence } => {
400                assert_eq!(*sequence, 0);
401                // Simulate receive on the other side
402                let mut ch2 = Channel::new(0.1);
403                let recv_actions = ch2.receive(raw, 1.1);
404                assert_eq!(recv_actions.len(), 1);
405                match &recv_actions[0] {
406                    ChannelAction::MessageReceived {
407                        msgtype,
408                        payload,
409                        sequence,
410                    } => {
411                        assert_eq!(*msgtype, 0x01);
412                        assert_eq!(payload, b"hello");
413                        assert_eq!(*sequence, 0);
414                    }
415                    _ => panic!("Expected MessageReceived"),
416                }
417            }
418            _ => panic!("Expected SendOnLink"),
419        }
420    }
421
422    #[test]
423    fn test_send_not_ready() {
424        let mut ch = Channel::new(0.1);
425        // Fill the window
426        ch.send(0x01, b"a", 1.0, 500).unwrap();
427        ch.send(0x01, b"b", 1.0, 500).unwrap();
428        // Window = 2, both outstanding
429        assert!(!ch.is_ready_to_send());
430        assert_eq!(ch.send(0x01, b"c", 1.0, 500), Err(ChannelError::NotReady));
431    }
432
433    #[test]
434    fn test_message_too_big_does_not_consume_sequence() {
435        let mut ch = Channel::new(0.1);
436        assert_eq!(
437            ch.send(0x01, b"hello", 1.0, 2),
438            Err(ChannelError::MessageTooBig)
439        );
440
441        let actions = ch.send(0x01, b"ok", 2.0, 500).unwrap();
442        match &actions[0] {
443            ChannelAction::SendOnLink { sequence, .. } => assert_eq!(*sequence, 0),
444            _ => panic!("Expected SendOnLink"),
445        }
446    }
447
448    #[test]
449    fn test_cancel_send_rewinds_sequence_and_frees_window() {
450        let mut ch = Channel::new(CHANNEL_RTT_SLOW + 1.0);
451        let actions = ch.send(0x01, b"first", 1.0, 500).unwrap();
452        let sequence = match &actions[0] {
453            ChannelAction::SendOnLink { sequence, .. } => *sequence,
454            _ => panic!("Expected SendOnLink"),
455        };
456        assert!(!ch.is_ready_to_send());
457
458        assert!(ch.cancel_send(sequence));
459        assert!(ch.is_ready_to_send());
460        let actions = ch.send(0x01, b"retry", 2.0, 500).unwrap();
461        match &actions[0] {
462            ChannelAction::SendOnLink { sequence, .. } => assert_eq!(*sequence, 0),
463            _ => panic!("Expected SendOnLink"),
464        }
465    }
466
467    #[test]
468    fn test_packet_delivered_grows_window() {
469        let mut ch = Channel::new(0.1);
470        ch.send(0x01, b"a", 1.0, 500).unwrap();
471        ch.send(0x01, b"b", 1.0, 500).unwrap();
472
473        assert_eq!(ch.window, 2);
474        ch.packet_delivered(0);
475        assert_eq!(ch.window, 3);
476    }
477
478    #[test]
479    fn test_packet_timeout_shrinks_window() {
480        let mut ch = Channel::new(0.1);
481        ch.send(0x01, b"a", 1.0, 500).unwrap();
482        ch.send(0x01, b"b", 1.0, 500).unwrap();
483
484        // Deliver one to grow window
485        ch.packet_delivered(0);
486        assert_eq!(ch.window, 3);
487
488        // Timeout on seq 1
489        let actions = ch.packet_timeout(1, 2.0);
490        assert_eq!(actions.len(), 1); // resend
491        assert_eq!(ch.window, 2);
492    }
493
494    #[test]
495    fn test_tick_retransmits_timed_out_packets() {
496        let mut ch = Channel::new(0.1);
497        ch.send(0x01, b"a", 0.0, 500).unwrap();
498
499        let timeout = ch.get_packet_timeout(1);
500        let actions = ch.tick(timeout + 0.01);
501        assert_eq!(actions.len(), 1);
502        match &actions[0] {
503            ChannelAction::SendOnLink { sequence, .. } => assert_eq!(*sequence, 0),
504            _ => panic!("Expected SendOnLink"),
505        }
506        assert_eq!(ch.get_tries(0), Some(2));
507    }
508
509    #[test]
510    fn test_max_retries_teardown() {
511        let mut ch = Channel::new(0.1);
512        ch.send(0x01, b"a", 1.0, 500).unwrap();
513
514        // Time out until max_tries exceeded
515        for i in 0..4 {
516            let actions = ch.packet_timeout(0, 2.0 + i as f64);
517            assert_eq!(actions.len(), 1);
518            match &actions[0] {
519                ChannelAction::SendOnLink { .. } => {}
520                _ => panic!("Expected SendOnLink"),
521            }
522        }
523
524        // One more timeout → teardown
525        let actions = ch.packet_timeout(0, 10.0);
526        assert_eq!(actions.len(), 1);
527        match &actions[0] {
528            ChannelAction::TeardownLink => {}
529            _ => panic!("Expected TeardownLink"),
530        }
531    }
532
533    #[test]
534    fn test_sequence_wrapping() {
535        let mut ch = Channel::new(0.1);
536        ch.next_sequence = CHANNEL_SEQ_MAX;
537
538        ch.send(0x01, b"wrap", 1.0, 500).unwrap();
539        assert_eq!(ch.next_sequence, 0);
540
541        ch.send(0x01, b"after", 1.0, 500).unwrap();
542        assert_eq!(ch.next_sequence, 1);
543    }
544
545    #[test]
546    fn test_out_of_order_buffering() {
547        let mut ch = Channel::new(0.1);
548
549        // Send messages out of order (simulate): sequence 1 arrives before 0
550        let raw0 = pack_envelope(0x01, 0, b"first");
551        let raw1 = pack_envelope(0x01, 1, b"second");
552
553        // Receive seq 1 first
554        let actions = ch.receive(&raw1, 1.0);
555        assert!(actions.is_empty()); // buffered, waiting for 0
556
557        // Receive seq 0
558        let actions = ch.receive(&raw0, 1.1);
559        assert_eq!(actions.len(), 2); // both delivered in order
560        match &actions[0] {
561            ChannelAction::MessageReceived { sequence, .. } => assert_eq!(*sequence, 0),
562            _ => panic!("Expected MessageReceived"),
563        }
564        match &actions[1] {
565            ChannelAction::MessageReceived { sequence, .. } => assert_eq!(*sequence, 1),
566            _ => panic!("Expected MessageReceived"),
567        }
568    }
569
570    #[test]
571    fn receive_window_accepts_exact_forward_edge_and_rejects_beyond_it() {
572        let mut ch = Channel::new(0.1);
573        let edge = CHANNEL_WINDOW_MAX_FAST;
574        let beyond = edge + 1;
575
576        assert!(ch
577            .receive(&pack_envelope(0x01, edge, b"edge"), 1.0)
578            .is_empty());
579        assert_eq!(ch.rx_ring.len(), 1);
580        assert!(ch
581            .receive(&pack_envelope(0x01, beyond, b"beyond"), 1.1)
582            .is_empty());
583        assert_eq!(ch.rx_ring.len(), 1);
584        assert!(ch
585            .receive(&pack_envelope(0x01, 40_000, b"far"), 1.2)
586            .is_empty());
587        assert_eq!(ch.rx_ring.len(), 1);
588    }
589
590    #[test]
591    fn receive_window_enforces_modular_edge_across_sequence_wrap() {
592        let mut ch = Channel::new(0.1);
593        ch.next_rx_sequence = 0xFFFE;
594        let edge = ch.next_rx_sequence.wrapping_add(CHANNEL_WINDOW_MAX_FAST);
595        let beyond = edge.wrapping_add(1);
596        let behind = ch.next_rx_sequence.wrapping_sub(1);
597
598        assert!(ch
599            .receive(&pack_envelope(0x01, edge, b"edge"), 1.0)
600            .is_empty());
601        assert_eq!(ch.rx_ring.len(), 1);
602        assert!(ch
603            .receive(&pack_envelope(0x01, beyond, b"beyond"), 1.1)
604            .is_empty());
605        assert!(ch
606            .receive(&pack_envelope(0x01, behind, b"behind"), 1.2)
607            .is_empty());
608        assert_eq!(ch.rx_ring.len(), 1);
609    }
610
611    #[test]
612    fn test_duplicate_rejection() {
613        let mut ch = Channel::new(0.1);
614        let raw = pack_envelope(0x01, 0, b"hello");
615
616        let actions = ch.receive(&raw, 1.0);
617        assert_eq!(actions.len(), 1);
618
619        // Duplicate
620        let actions = ch.receive(&raw, 1.1);
621        assert!(actions.is_empty());
622    }
623
624    #[test]
625    fn test_get_packet_timeout() {
626        let ch = Channel::new(0.1);
627        let t1 = ch.get_packet_timeout(1);
628        let t2 = ch.get_packet_timeout(2);
629        assert!(t2 > t1); // exponential backoff
630    }
631
632    #[test]
633    fn test_mdu() {
634        let ch = Channel::new(0.1);
635        assert_eq!(ch.mdu(431), 431 - CHANNEL_ENVELOPE_OVERHEAD);
636    }
637
638    #[test]
639    fn test_window_upgrade_fast() {
640        let mut ch = Channel::new(0.05); // fast RTT
641        ch.window_max = CHANNEL_WINDOW_MAX_SLOW;
642
643        // Deliver FAST_RATE_THRESHOLD messages
644        for i in 0..CHANNEL_FAST_RATE_THRESHOLD {
645            ch.send(0x01, b"x", i as f64, 500).unwrap();
646            ch.packet_delivered(i);
647        }
648
649        assert_eq!(ch.window_max, CHANNEL_WINDOW_MAX_FAST);
650        assert_eq!(ch.window_min, CHANNEL_WINDOW_MIN_LIMIT_FAST);
651    }
652
653    #[test]
654    fn test_window_upgrade_medium() {
655        let mut ch = Channel::new(0.5); // medium RTT
656        ch.window_max = CHANNEL_WINDOW_MAX_SLOW;
657
658        for i in 0..CHANNEL_FAST_RATE_THRESHOLD {
659            ch.send(0x01, b"x", i as f64, 500).unwrap();
660            ch.packet_delivered(i);
661        }
662
663        assert_eq!(ch.window_max, CHANNEL_WINDOW_MAX_MEDIUM);
664        assert_eq!(ch.window_min, CHANNEL_WINDOW_MIN_LIMIT_MEDIUM);
665    }
666
667    #[test]
668    fn test_shutdown() {
669        let mut ch = Channel::new(0.1);
670        ch.send(0x01, b"a", 1.0, 500).unwrap();
671        ch.shutdown();
672        assert_eq!(ch.outstanding(), 0);
673    }
674
675    #[test]
676    fn test_message_too_big() {
677        let mut ch = Channel::new(0.1);
678        let big = vec![0u8; 500];
679        // link_mdu = 10, message + header won't fit
680        assert_eq!(
681            ch.send(0x01, &big, 1.0, 10),
682            Err(ChannelError::MessageTooBig)
683        );
684    }
685
686    #[test]
687    fn test_receive_sequence_wrap_at_boundary() {
688        let mut ch = Channel::new(0.1);
689        ch.next_rx_sequence = CHANNEL_SEQ_MAX;
690
691        let raw_max = pack_envelope(0x01, CHANNEL_SEQ_MAX, b"last");
692        let raw_zero = pack_envelope(0x01, 0, b"first_after_wrap");
693
694        let actions = ch.receive(&raw_max, 1.0);
695        assert_eq!(actions.len(), 1);
696        assert_eq!(ch.next_rx_sequence, 0);
697
698        let actions = ch.receive(&raw_zero, 1.1);
699        assert_eq!(actions.len(), 1);
700        match &actions[0] {
701            ChannelAction::MessageReceived { sequence, .. } => assert_eq!(*sequence, 0),
702            _ => panic!("Expected MessageReceived"),
703        }
704    }
705
706    #[test]
707    fn test_receive_wrap_boundary_out_of_order() {
708        // Test that out-of-order messages at the wrap boundary (0xFFFF→0) are sorted correctly.
709        let mut ch = Channel::new(0.1);
710        ch.next_rx_sequence = 0xFFFE;
711
712        let raw_fffe = pack_envelope(0x01, 0xFFFE, b"a");
713        let raw_ffff = pack_envelope(0x01, 0xFFFF, b"b");
714        let raw_0000 = pack_envelope(0x01, 0x0000, b"c");
715
716        // Deliver in reverse order: 0, 0xFFFF, 0xFFFE
717        let actions = ch.receive(&raw_0000, 1.0);
718        assert!(actions.is_empty()); // waiting for 0xFFFE
719
720        let actions = ch.receive(&raw_ffff, 1.1);
721        assert!(actions.is_empty()); // still waiting for 0xFFFE
722
723        let actions = ch.receive(&raw_fffe, 1.2);
724        assert_eq!(actions.len(), 3); // all three delivered in order
725        match &actions[0] {
726            ChannelAction::MessageReceived {
727                sequence, payload, ..
728            } => {
729                assert_eq!(*sequence, 0xFFFE);
730                assert_eq!(payload, b"a");
731            }
732            _ => panic!("Expected MessageReceived"),
733        }
734        match &actions[1] {
735            ChannelAction::MessageReceived {
736                sequence, payload, ..
737            } => {
738                assert_eq!(*sequence, 0xFFFF);
739                assert_eq!(payload, b"b");
740            }
741            _ => panic!("Expected MessageReceived"),
742        }
743        match &actions[2] {
744            ChannelAction::MessageReceived {
745                sequence, payload, ..
746            } => {
747                assert_eq!(*sequence, 0x0000);
748                assert_eq!(payload, b"c");
749            }
750            _ => panic!("Expected MessageReceived"),
751        }
752    }
753
754    #[test]
755    fn test_many_messages_in_order() {
756        let mut sender = Channel::new(0.05);
757        let mut receiver = Channel::new(0.05);
758
759        for i in 0..20u16 {
760            // Deliver previous to make window available
761            if i >= 2 {
762                sender.packet_delivered(i - 2);
763            }
764
765            let actions = sender.send(0x01, &[i as u8], i as f64, 500).unwrap();
766            let raw = match &actions[0] {
767                ChannelAction::SendOnLink { raw, .. } => raw.clone(),
768                _ => panic!("Expected SendOnLink"),
769            };
770
771            let recv_actions = receiver.receive(&raw, i as f64 + 0.1);
772            assert_eq!(recv_actions.len(), 1);
773            match &recv_actions[0] {
774                ChannelAction::MessageReceived {
775                    payload, sequence, ..
776                } => {
777                    assert_eq!(*sequence, i);
778                    assert_eq!(payload, &[i as u8]);
779                }
780                _ => panic!("Expected MessageReceived"),
781            }
782        }
783    }
784}