Skip to main content

rns_net/
link_send.rs

1//! Runtime-independent transmission completion and bounded admission.
2use std::future::Future;
3use std::pin::Pin;
4use std::sync::{Arc, Mutex, Weak};
5use std::task::{Context, Poll, Waker};
6
7use futures::channel::oneshot;
8
9/// A local send failure. Success never implies acknowledgement by the peer.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum LinkSendError {
12    QueueFull,
13    DriverStopped,
14    Interrupted,
15    Draining,
16    InvalidPacket(crate::event::LinkDatagramError),
17    PacketBuildFailed,
18    NoRoute,
19    Rejected,
20    InterfaceUnavailable,
21    WriteFailed(String),
22}
23
24impl std::fmt::Display for LinkSendError {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            Self::QueueFull => f.write_str("outbound queue is full"),
28            Self::DriverStopped => f.write_str("driver stopped before transmission completed"),
29            Self::Interrupted => f.write_str("transmission interrupted before completion"),
30            Self::Draining => f.write_str("node is draining"),
31            Self::InvalidPacket(error) => write!(f, "{error}"),
32            Self::PacketBuildFailed => f.write_str("failed to build or encrypt outbound packet"),
33            Self::NoRoute => f.write_str("no outgoing interface route"),
34            Self::Rejected => f.write_str("outbound packet rejected by routing or policy"),
35            Self::InterfaceUnavailable => f.write_str("outbound interface is unavailable"),
36            Self::WriteFailed(error) => write!(f, "interface write failed: {error}"),
37        }
38    }
39}
40impl std::error::Error for LinkSendError {}
41
42/// Await this receipt to learn whether all selected interfaces finished writing.
43/// Obtaining a receipt means admission only; it is not a transmission result.
44/// Dropping a receipt does not cancel an admitted send.
45#[must_use = "await the receipt (or call wait) to observe transmission completion"]
46pub struct LinkSendReceipt(oneshot::Receiver<Result<(), LinkSendError>>);
47
48impl Future for LinkSendReceipt {
49    type Output = Result<(), LinkSendError>;
50    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
51        Pin::new(&mut self.0)
52            .poll(cx)
53            .map(|result| result.unwrap_or(Err(LinkSendError::Interrupted)))
54    }
55}
56
57impl LinkSendReceipt {
58    /// Blocking counterpart for synchronous callers. Do not call from a driver callback.
59    pub fn wait(self) -> Result<(), LinkSendError> {
60        futures::executor::block_on(self)
61    }
62}
63
64/// A packet's hash is available immediately for proof tracking, but successful
65/// transmission is reported only when this receipt resolves. Failure may follow
66/// partial transmission; neither the hash nor success promises remote delivery.
67#[must_use = "await the receipt (or call wait) to observe transmission completion"]
68pub struct PacketSendReceipt {
69    pub(crate) completion: LinkSendReceipt,
70    pub(crate) hash: rns_core::types::PacketHash,
71}
72impl PacketSendReceipt {
73    pub fn packet_hash(&self) -> rns_core::types::PacketHash {
74        self.hash
75    }
76    /// Blocking completion wait. Do not call from a driver callback.
77    pub fn wait(self) -> Result<rns_core::types::PacketHash, LinkSendError> {
78        futures::executor::block_on(self)
79    }
80}
81impl Future for PacketSendReceipt {
82    type Output = Result<rns_core::types::PacketHash, LinkSendError>;
83    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
84        let hash = self.hash;
85        Pin::new(&mut self.completion)
86            .poll(cx)
87            .map(|r| r.map(|()| hash))
88    }
89}
90
91#[derive(Default)]
92pub(crate) struct Waiter(Mutex<Option<Waker>>);
93
94impl Waiter {
95    pub(crate) fn register(&self, cx: &Context<'_>) {
96        *self.0.lock().unwrap() = Some(cx.waker().clone());
97    }
98    pub(crate) fn wake(&self) {
99        let waker = self.0.lock().unwrap().take();
100        if let Some(waker) = waker {
101            waker.wake();
102        }
103    }
104}
105
106struct PoolState {
107    used: usize,
108    closed: bool,
109    waiters: Vec<Weak<Waiter>>,
110    completions: Vec<Weak<CompletionState>>,
111    links: std::collections::HashMap<[u8; 16], SendCounts>,
112}
113
114#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
115pub(crate) struct SendCounts {
116    pub pending: usize,
117    pub waiting: usize,
118}
119
120enum SendPhase {
121    Unadmitted,
122    Waiting,
123    Pending,
124}
125
126pub(crate) struct SendTracking {
127    pool: Arc<SendPool>,
128    link_id: [u8; 16],
129    phase: SendPhase,
130}
131
132impl SendTracking {
133    fn admit(&mut self) {
134        let mut state = self.pool.state.lock().unwrap();
135        if state.closed {
136            return;
137        }
138        let counts = state.links.entry(self.link_id).or_default();
139        match self.phase {
140            SendPhase::Waiting => counts.waiting -= 1,
141            SendPhase::Pending => return,
142            SendPhase::Unadmitted => {}
143        }
144        counts.pending += 1;
145        self.phase = SendPhase::Pending;
146    }
147}
148
149impl Drop for SendTracking {
150    fn drop(&mut self) {
151        let mut state = self.pool.state.lock().unwrap();
152        if let Some(counts) = state.links.get_mut(&self.link_id) {
153            match self.phase {
154                SendPhase::Waiting => counts.waiting -= 1,
155                SendPhase::Pending => counts.pending -= 1,
156                SendPhase::Unadmitted => {}
157            }
158            if counts.pending == 0 && counts.waiting == 0 {
159                state.links.remove(&self.link_id);
160            }
161        }
162    }
163}
164
165pub(crate) struct SendPool {
166    capacity: usize,
167    state: Mutex<PoolState>,
168}
169
170impl SendPool {
171    pub(crate) fn new(capacity: usize) -> Arc<Self> {
172        Arc::new(Self {
173            capacity: capacity.max(1),
174            state: Mutex::new(PoolState {
175                used: 0,
176                closed: false,
177                waiters: Vec::new(),
178                completions: Vec::new(),
179                links: std::collections::HashMap::new(),
180            }),
181        })
182    }
183
184    pub(crate) fn try_acquire(self: &Arc<Self>) -> Result<Permit, LinkSendError> {
185        let mut state = self.state.lock().unwrap();
186        if state.closed {
187            return Err(LinkSendError::DriverStopped);
188        }
189        if state.used == self.capacity {
190            return Err(LinkSendError::QueueFull);
191        }
192        state.used += 1;
193        Ok(Permit(self.clone()))
194    }
195
196    pub(crate) fn track(self: &Arc<Self>, link_id: [u8; 16], waiting: bool) -> SendTracking {
197        let mut state = self.state.lock().unwrap();
198        let phase = if waiting && !state.closed {
199            state.links.entry(link_id).or_default().waiting += 1;
200            SendPhase::Waiting
201        } else {
202            SendPhase::Unadmitted
203        };
204        SendTracking {
205            pool: self.clone(),
206            link_id,
207            phase,
208        }
209    }
210
211    pub(crate) fn snapshot(&self) -> std::collections::HashMap<[u8; 16], SendCounts> {
212        self.state.lock().unwrap().links.clone()
213    }
214
215    pub(crate) async fn acquire(self: &Arc<Self>) -> Result<Permit, LinkSendError> {
216        let waiter = Arc::new(Waiter::default());
217        {
218            let mut state = self.state.lock().unwrap();
219            state.waiters.retain(|waiter| waiter.strong_count() > 0);
220            state.waiters.push(Arc::downgrade(&waiter));
221        }
222        futures::future::poll_fn(|cx| {
223            waiter.register(cx);
224            match self.try_acquire() {
225                Err(LinkSendError::QueueFull) => Poll::Pending,
226                result => Poll::Ready(result),
227            }
228        })
229        .await
230    }
231
232    pub(crate) fn close(&self) {
233        let (waiters, completions) = {
234            let mut state = self.state.lock().unwrap();
235            state.closed = true;
236            state.links.clear();
237            (
238                std::mem::take(&mut state.waiters),
239                std::mem::take(&mut state.completions),
240            )
241        };
242        for waiter in waiters.into_iter().filter_map(|w| w.upgrade()) {
243            waiter.wake();
244        }
245        for completion in completions.into_iter().filter_map(|w| w.upgrade()) {
246            completion.finish(Err(LinkSendError::DriverStopped));
247        }
248    }
249
250    pub(crate) fn in_flight(&self) -> usize {
251        self.state.lock().unwrap().used
252    }
253}
254
255pub(crate) struct Permit(Arc<SendPool>);
256impl Drop for Permit {
257    fn drop(&mut self) {
258        let waiters = {
259            let mut state = self.0.state.lock().unwrap();
260            state.used -= 1;
261            state.waiters.retain(|waiter| waiter.strong_count() > 0);
262            state.waiters.clone()
263        };
264        for waiter in waiters.into_iter().filter_map(|w| w.upgrade()) {
265            waiter.wake();
266        }
267    }
268}
269
270/// Internal writer completion token. Dropping it reports an interrupted send.
271#[doc(hidden)]
272pub struct Completion {
273    ratchet_lease: Option<crate::local_ratchet::AnnouncementLease>,
274    state: Arc<CompletionState>,
275    settled: bool,
276}
277
278struct CompletionData {
279    tx: oneshot::Sender<Result<(), LinkSendError>>,
280    tracking: Option<SendTracking>,
281    permit: Permit,
282    remaining: usize,
283    error: Option<LinkSendError>,
284}
285struct CompletionState(Mutex<Option<CompletionData>>);
286impl Drop for CompletionState {
287    fn drop(&mut self) {
288        // Clear counters before waking a receipt whose writer/event vanished.
289        self.finish(Err(LinkSendError::Interrupted));
290    }
291}
292impl CompletionState {
293    fn finish(&self, result: Result<(), LinkSendError>) {
294        let data = self.0.lock().unwrap().take();
295        if let Some(data) = data {
296            drop(data.tracking);
297            drop(data.permit);
298            let _ = data.tx.send(result);
299        }
300    }
301}
302
303impl Completion {
304    pub(crate) fn retain_ratchet(
305        &mut self,
306        lease: Option<crate::local_ratchet::AnnouncementLease>,
307    ) {
308        self.ratchet_lease = lease;
309    }
310
311    pub(crate) fn new(permit: Permit) -> (Self, LinkSendReceipt) {
312        Self::new_inner(permit, None)
313    }
314
315    pub(crate) fn new_tracked(permit: Permit, tracking: SendTracking) -> (Self, LinkSendReceipt) {
316        Self::new_inner(permit, Some(tracking))
317    }
318
319    fn new_inner(permit: Permit, tracking: Option<SendTracking>) -> (Self, LinkSendReceipt) {
320        let (tx, rx) = oneshot::channel();
321        let pool = permit.0.clone();
322        let completion = Arc::new(CompletionState(Mutex::new(Some(CompletionData {
323            tx,
324            tracking,
325            permit,
326            remaining: 1,
327            error: None,
328        }))));
329        {
330            let mut pool = pool.state.lock().unwrap();
331            pool.completions.retain(|w| w.strong_count() > 0);
332            if pool.closed {
333                drop(pool);
334                completion.finish(Err(LinkSendError::DriverStopped));
335            } else {
336                pool.completions.push(Arc::downgrade(&completion));
337            }
338        }
339        (
340            Self {
341                ratchet_lease: None,
342                state: completion,
343                settled: false,
344            },
345            LinkSendReceipt(rx),
346        )
347    }
348    pub(crate) fn finish(mut self, result: Result<(), LinkSendError>) {
349        self.settle(result);
350    }
351
352    /// Each selected interface gets a branch. The permit and receipt remain
353    /// live until every branch settles, even if one branch fails early.
354    pub(crate) fn branch(&self) -> Self {
355        if let Some(data) = self.state.0.lock().unwrap().as_mut() {
356            data.remaining += 1;
357        }
358        Self {
359            ratchet_lease: self.ratchet_lease.clone(),
360            state: self.state.clone(),
361            settled: false,
362        }
363    }
364
365    fn settle(&mut self, result: Result<(), LinkSendError>) {
366        // Release this writer's key lease before waking a successful receipt;
367        // the application may immediately rotate/prune after awaiting it.
368        self.ratchet_lease = None;
369        self.settled = true;
370        let final_data = {
371            let mut guard = self.state.0.lock().unwrap();
372            let Some(data) = guard.as_mut() else { return };
373            if data.error.is_none() {
374                data.error = result.err();
375            }
376            data.remaining -= 1;
377            if data.remaining == 0 {
378                guard.take()
379            } else {
380                None
381            }
382        };
383        if let Some(data) = final_data {
384            drop(data.tracking);
385            drop(data.permit);
386            let _ = data.tx.send(data.error.map_or(Ok(()), Err));
387        }
388    }
389    pub(crate) fn is_finished(&self) -> bool {
390        self.state.0.lock().unwrap().is_none()
391    }
392
393    pub(crate) fn admit(&self) {
394        if let Some(data) = self.state.0.lock().unwrap().as_mut() {
395            if let Some(tracking) = data.tracking.as_mut() {
396                tracking.admit();
397            }
398        }
399    }
400}
401
402impl Drop for Completion {
403    fn drop(&mut self) {
404        if !self.settled {
405            self.settle(Err(LinkSendError::Interrupted));
406        }
407    }
408}
409
410#[cfg(test)]
411mod tests {
412    use super::*;
413    use futures::{executor::block_on, FutureExt};
414
415    #[test]
416    fn fanout_keeps_capacity_until_all_writes_settle() {
417        let pool = SendPool::new(1);
418        let (completion, mut receipt) = Completion::new(pool.try_acquire().unwrap());
419        let first = completion.branch();
420        let last = completion.branch();
421        completion.finish(Ok(()));
422        first.finish(Err(LinkSendError::WriteFailed("first failed".into())));
423        assert!((&mut receipt).now_or_never().is_none());
424        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
425        last.finish(Ok(()));
426        assert_eq!(
427            receipt.wait(),
428            Err(LinkSendError::WriteFailed("first failed".into()))
429        );
430        assert_eq!(pool.in_flight(), 0);
431
432        let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
433        let child = completion.branch();
434        completion.finish(Ok(()));
435        drop(child);
436        assert_eq!(receipt.wait(), Err(LinkSendError::Interrupted));
437        assert_eq!(pool.in_flight(), 0);
438
439        let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
440        let child = completion.branch();
441        pool.close();
442        assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
443        assert_eq!(pool.in_flight(), 0);
444        drop((completion, child));
445    }
446
447    #[test]
448    fn packet_and_announce_admission_is_bounded_and_runtime_independent() {
449        use crate::event::Event;
450        let (tx, rx) = crate::event::channel_with_capacity(1);
451        let node = crate::RnsNode::from_parts(
452            tx.clone(),
453            std::thread::spawn(|| {}),
454            None,
455            Arc::new(std::sync::atomic::AtomicU64::new(1000)),
456        );
457        let dest = crate::Destination::plain("confirmed", &["packet"]);
458        let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::OsRng);
459        let announce_dest = crate::Destination::single_in(
460            "confirmed",
461            &["announce"],
462            crate::IdentityHash(*identity.hash()),
463        );
464        drop(node.announce(&announce_dest, &identity, None));
465        assert!(
466            rx.try_recv().is_err(),
467            "unpolled sends must have no side effects"
468        );
469        tx.send(Event::Tick).unwrap();
470        assert!(matches!(
471            node.try_announce(&announce_dest, &identity, None),
472            Err(LinkSendError::QueueFull)
473        ));
474        let mut cancelled = Box::pin(node.announce(&announce_dest, &identity, None));
475        assert!(cancelled.as_mut().now_or_never().is_none());
476        drop(cancelled);
477        assert_eq!(tx.link_send_pool().in_flight(), 0);
478        assert!(matches!(rx.try_recv(), Ok(Event::Tick)));
479        assert!(
480            rx.try_recv().is_err(),
481            "cancelled admission must not update replay state"
482        );
483        let receipt = node.try_send_packet(&dest, b"first").unwrap();
484        let hash = receipt.packet_hash();
485        let Event::SendOutboundTracked {
486            completion: first, ..
487        } = rx.try_recv().unwrap()
488        else {
489            panic!("expected tracked packet")
490        };
491        assert!(matches!(
492            node.try_announce(&announce_dest, &identity, None),
493            Err(LinkSendError::QueueFull)
494        ));
495        let mut announce = Box::pin(node.announce(&announce_dest, &identity, None));
496        assert!(announce.as_mut().now_or_never().is_none());
497        assert!(rx.try_recv().is_err());
498        first.finish(Ok(()));
499        assert_eq!(receipt.wait().unwrap(), hash);
500        assert!(announce.as_mut().now_or_never().is_none());
501        let Event::SendOutboundTracked {
502            completion, replay, ..
503        } = rx.try_recv().unwrap()
504        else {
505            panic!("expected tracked announce")
506        };
507        assert_eq!(replay.unwrap().dest_hash, announce_dest.hash.0);
508        assert!(matches!(
509            node.try_send_packet(&dest, b"full"),
510            Err(LinkSendError::QueueFull)
511        ));
512        completion.finish(Ok(()));
513        block_on(announce).unwrap();
514        let receipt = node.try_announce(&announce_dest, &identity, None).unwrap();
515        let Event::SendOutboundTracked { completion, .. } = rx.try_recv().unwrap() else {
516            panic!("expected tracked announce")
517        };
518        completion.finish(Ok(()));
519        receipt.wait().unwrap();
520        let mut packet = Box::pin(node.send_packet(&dest, b"async"));
521        assert!(packet.as_mut().now_or_never().is_none());
522        let Event::SendOutboundTracked {
523            completion, raw, ..
524        } = rx.try_recv().unwrap()
525        else {
526            panic!("expected tracked packet")
527        };
528        let hash = rns_core::types::PacketHash(
529            rns_core::packet::RawPacket::unpack(&raw)
530                .unwrap()
531                .packet_hash,
532        );
533        completion.finish(Ok(()));
534        assert_eq!(block_on(packet).unwrap(), hash);
535        assert!(
536            tx.link_send_pool().snapshot().is_empty(),
537            "non-Link sends must not affect per-Link counts"
538        );
539        drop(rx);
540        assert!(matches!(
541            node.try_send_packet(&dest, b"stopped"),
542            Err(LinkSendError::DriverStopped)
543        ));
544        assert_eq!(
545            block_on(node.announce(&announce_dest, &identity, None)),
546            Err(LinkSendError::DriverStopped)
547        );
548    }
549
550    #[test]
551    fn per_link_counts_cover_failure_shutdown_and_receipt_cancellation() {
552        let pool = SendPool::new(3);
553        let waiting = pool.track([1; 16], true);
554        let tracking = pool.track([2; 16], true);
555        let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
556        completion.admit();
557        assert_eq!(
558            pool.snapshot().get(&[1; 16]),
559            Some(&SendCounts {
560                pending: 0,
561                waiting: 1
562            })
563        );
564        assert_eq!(
565            pool.snapshot().get(&[2; 16]),
566            Some(&SendCounts {
567                pending: 1,
568                waiting: 0
569            })
570        );
571        drop(receipt);
572        assert_eq!(
573            pool.snapshot()[&[2; 16]].pending,
574            1,
575            "dropping a receipt does not cancel transmission"
576        );
577        completion.finish(Err(LinkSendError::WriteFailed("broken pipe".into())));
578        assert!(!pool.snapshot().contains_key(&[2; 16]));
579        drop(waiting);
580        assert!(pool.snapshot().is_empty());
581
582        let tracking = pool.track([4; 16], false);
583        let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
584        completion.admit();
585        drop(completion);
586        assert_eq!(receipt.wait(), Err(LinkSendError::Interrupted));
587        assert!(pool.snapshot().is_empty());
588
589        let tracking = pool.track([3; 16], false);
590        let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
591        completion.admit();
592        let waiting = pool.track([3; 16], true);
593        pool.close();
594        assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
595        assert!(pool.snapshot().is_empty());
596        drop(waiting);
597        drop(completion);
598        assert!(pool.snapshot().is_empty());
599    }
600
601    #[test]
602    fn node_try_rejects_full_capacity_and_async_send_wakes_without_blocking() {
603        use crate::event::Event;
604        use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
605        struct WakeCount(AtomicUsize);
606        impl futures::task::ArcWake for WakeCount {
607            fn wake_by_ref(this: &Arc<Self>) {
608                this.0.fetch_add(1, Ordering::SeqCst);
609            }
610        }
611        let (tx, rx) = crate::event::channel_with_capacity(1);
612        let node = crate::RnsNode::from_parts(
613            tx.clone(),
614            std::thread::spawn(|| {}),
615            None,
616            Arc::new(AtomicU64::new(1000)),
617        );
618        let receipt = node.try_send_on_link([1; 16], vec![1], 0).unwrap();
619        assert_eq!(
620            tx.link_send_pool().snapshot()[&[1; 16]],
621            SendCounts {
622                pending: 1,
623                waiting: 0
624            }
625        );
626        let first = match rx.try_recv().unwrap() {
627            Event::SendLinkTracked { completion, .. } => completion,
628            _ => panic!("expected a tracked send"),
629        };
630        assert!(matches!(
631            node.try_send_on_link([1; 16], vec![2], 0),
632            Err(LinkSendError::QueueFull)
633        ));
634        let mut pending = Box::pin(node.send_on_link([1; 16], vec![2], 0));
635        let wakes = Arc::new(WakeCount(AtomicUsize::new(0)));
636        let waker = futures::task::waker(wakes.clone());
637        let mut cx = Context::from_waker(&waker);
638        assert!(pending.as_mut().poll(&mut cx).is_pending());
639        assert_eq!(
640            tx.link_send_pool().snapshot()[&[1; 16]],
641            SendCounts {
642                pending: 1,
643                waiting: 1
644            }
645        );
646        assert!(
647            rx.try_recv().is_err(),
648            "capacity wait must not enqueue prematurely"
649        );
650        first.finish(Ok(()));
651        receipt.wait().unwrap();
652        assert_eq!(
653            tx.link_send_pool().snapshot()[&[1; 16]],
654            SendCounts {
655                pending: 0,
656                waiting: 1
657            }
658        );
659        assert!(
660            wakes.0.load(Ordering::SeqCst) > 0,
661            "capacity release must wake the task"
662        );
663        assert!(pending.as_mut().poll(&mut cx).is_pending());
664        let second = match rx.try_recv().unwrap() {
665            Event::SendLinkTracked { completion, .. } => completion,
666            _ => panic!("expected a tracked send"),
667        };
668        assert_eq!(
669            tx.link_send_pool().snapshot()[&[1; 16]],
670            SendCounts {
671                pending: 1,
672                waiting: 0
673            }
674        );
675        second.finish(Ok(()));
676        assert!(tx.link_send_pool().snapshot().is_empty());
677        assert_eq!(pending.as_mut().poll(&mut cx), Poll::Ready(Ok(())));
678        drop(pending);
679
680        // A full *driver* event queue also makes try return immediately.
681        tx.try_send(Event::Tick).unwrap();
682        assert!(matches!(
683            node.try_send_on_link([1; 16], vec![3], 0),
684            Err(LinkSendError::QueueFull)
685        ));
686        let mut cancelled = Box::pin(node.send_on_link([1; 16], vec![3], 0));
687        assert!(cancelled.as_mut().poll(&mut cx).is_pending());
688        assert_eq!(
689            tx.link_send_pool().snapshot()[&[1; 16]],
690            SendCounts {
691                pending: 0,
692                waiting: 1
693            }
694        );
695        drop(cancelled);
696        assert!(tx.link_send_pool().snapshot().is_empty());
697        assert_eq!(tx.link_send_pool().in_flight(), 0);
698        assert!(matches!(rx.try_recv(), Ok(Event::Tick)));
699        assert!(
700            rx.try_recv().is_err(),
701            "cancelled admission must not send later"
702        );
703        drop(rx);
704        node.shutdown();
705    }
706
707    #[test]
708    fn capacity_waits_for_transmission_not_receipt_creation() {
709        let pool = SendPool::new(1);
710        let (completion, mut receipt) = Completion::new(pool.try_acquire().unwrap());
711        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
712        assert!((&mut receipt).now_or_never().is_none());
713        let mut waiting = Box::pin(pool.acquire());
714        assert!(waiting.as_mut().now_or_never().is_none());
715        completion.finish(Ok(()));
716        receipt.wait().unwrap();
717        let permit = block_on(waiting).unwrap();
718        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
719        drop(permit);
720        assert!(pool.try_acquire().is_ok());
721    }
722
723    #[test]
724    fn shutdown_resolves_in_flight_receipts_and_capacity_waiters() {
725        let pool = SendPool::new(1);
726        let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
727        let mut waiting = Box::pin(pool.acquire());
728        assert!(waiting.as_mut().now_or_never().is_none());
729        pool.close();
730        assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
731        assert!(matches!(
732            block_on(waiting),
733            Err(LinkSendError::DriverStopped)
734        ));
735        // A delayed worker cannot overwrite the shutdown result.
736        completion.finish(Ok(()));
737    }
738
739    #[test]
740    fn dropping_receipt_does_not_release_an_in_flight_slot() {
741        let pool = SendPool::new(1);
742        let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
743        drop(receipt);
744        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
745        drop(completion);
746        assert!(pool.try_acquire().is_ok());
747    }
748
749    #[test]
750    fn both_node_apis_report_the_same_invalid_link_error() {
751        struct Callbacks;
752        impl crate::Callbacks for Callbacks {
753            fn on_announce(&mut self, _: crate::AnnouncedIdentity) {}
754            fn on_path_updated(&mut self, _: crate::DestHash, _: u8) {}
755            fn on_local_delivery(&mut self, _: crate::DestHash, _: Vec<u8>, _: crate::PacketHash) {}
756        }
757        let node =
758            crate::RnsNode::start(crate::NodeConfig::default(), Box::new(Callbacks)).unwrap();
759        let expected = Err(LinkSendError::InvalidPacket(
760            crate::event::LinkDatagramError::LinkNotFound,
761        ));
762        assert_eq!(
763            block_on(node.send_on_link([0x42; 16], vec![1], 0)),
764            expected
765        );
766        assert_eq!(
767            node.try_send_on_link([0x42; 16], vec![1], 0)
768                .unwrap()
769                .wait(),
770            expected
771        );
772        let dest = crate::Destination::plain("confirmed", &["no_route"]);
773        assert_eq!(
774            block_on(node.send_packet(&dest, b"data")),
775            Err(LinkSendError::NoRoute)
776        );
777        assert_eq!(
778            node.try_send_packet(&dest, b"data").unwrap().wait(),
779            Err(LinkSendError::NoRoute)
780        );
781        let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::OsRng);
782        let announce_dest = crate::Destination::single_in(
783            "confirmed",
784            &["announce"],
785            crate::IdentityHash(*identity.hash()),
786        );
787        assert_eq!(
788            block_on(node.announce(&announce_dest, &identity, None)),
789            Err(LinkSendError::NoRoute)
790        );
791        assert_eq!(
792            node.try_announce(&announce_dest, &identity, None)
793                .unwrap()
794                .wait(),
795            Err(LinkSendError::NoRoute)
796        );
797        let oversized = vec![0; rns_core::constants::MTU * 2];
798        assert_eq!(
799            block_on(node.send_packet(&dest, &oversized)),
800            Err(LinkSendError::PacketBuildFailed)
801        );
802        assert!(matches!(
803            node.try_send_packet(&dest, &oversized),
804            Err(LinkSendError::PacketBuildFailed)
805        ));
806        node.begin_drain(std::time::Duration::from_secs(30))
807            .unwrap();
808        node.drain_status().unwrap();
809        assert_eq!(
810            block_on(node.send_packet(&dest, b"data")),
811            Err(LinkSendError::Draining)
812        );
813        assert_eq!(
814            node.try_announce(&announce_dest, &identity, None)
815                .unwrap()
816                .wait(),
817            Err(LinkSendError::Draining)
818        );
819        node.shutdown();
820    }
821}