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            Arc::new(std::sync::atomic::AtomicBool::new(true)),
457        );
458        let dest = crate::Destination::plain("confirmed", &["packet"]);
459        let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::OsRng);
460        let announce_dest = crate::Destination::single_in(
461            "confirmed",
462            &["announce"],
463            crate::IdentityHash(*identity.hash()),
464        );
465        drop(node.announce(&announce_dest, &identity, None));
466        assert!(
467            rx.try_recv().is_err(),
468            "unpolled sends must have no side effects"
469        );
470        tx.send(Event::Tick).unwrap();
471        assert!(matches!(
472            node.try_announce(&announce_dest, &identity, None),
473            Err(LinkSendError::QueueFull)
474        ));
475        let mut cancelled = Box::pin(node.announce(&announce_dest, &identity, None));
476        assert!(cancelled.as_mut().now_or_never().is_none());
477        drop(cancelled);
478        assert_eq!(tx.link_send_pool().in_flight(), 0);
479        assert!(matches!(rx.try_recv(), Ok(Event::Tick)));
480        assert!(
481            rx.try_recv().is_err(),
482            "cancelled admission must not update replay state"
483        );
484        let receipt = node.try_send_packet(&dest, b"first").unwrap();
485        let hash = receipt.packet_hash();
486        let Event::SendOutboundTracked {
487            completion: first, ..
488        } = rx.try_recv().unwrap()
489        else {
490            panic!("expected tracked packet")
491        };
492        assert!(matches!(
493            node.try_announce(&announce_dest, &identity, None),
494            Err(LinkSendError::QueueFull)
495        ));
496        let mut announce = Box::pin(node.announce(&announce_dest, &identity, None));
497        assert!(announce.as_mut().now_or_never().is_none());
498        assert!(rx.try_recv().is_err());
499        first.finish(Ok(()));
500        assert_eq!(receipt.wait().unwrap(), hash);
501        assert!(announce.as_mut().now_or_never().is_none());
502        let Event::SendOutboundTracked {
503            completion, replay, ..
504        } = rx.try_recv().unwrap()
505        else {
506            panic!("expected tracked announce")
507        };
508        assert_eq!(replay.unwrap().dest_hash, announce_dest.hash.0);
509        assert!(matches!(
510            node.try_send_packet(&dest, b"full"),
511            Err(LinkSendError::QueueFull)
512        ));
513        completion.finish(Ok(()));
514        block_on(announce).unwrap();
515        let receipt = node.try_announce(&announce_dest, &identity, None).unwrap();
516        let Event::SendOutboundTracked { completion, .. } = rx.try_recv().unwrap() else {
517            panic!("expected tracked announce")
518        };
519        completion.finish(Ok(()));
520        receipt.wait().unwrap();
521        let mut packet = Box::pin(node.send_packet(&dest, b"async"));
522        assert!(packet.as_mut().now_or_never().is_none());
523        let Event::SendOutboundTracked {
524            completion, raw, ..
525        } = rx.try_recv().unwrap()
526        else {
527            panic!("expected tracked packet")
528        };
529        let hash = rns_core::types::PacketHash(
530            rns_core::packet::RawPacket::unpack(&raw)
531                .unwrap()
532                .packet_hash,
533        );
534        completion.finish(Ok(()));
535        assert_eq!(block_on(packet).unwrap(), hash);
536        assert!(
537            tx.link_send_pool().snapshot().is_empty(),
538            "non-Link sends must not affect per-Link counts"
539        );
540        drop(rx);
541        assert!(matches!(
542            node.try_send_packet(&dest, b"stopped"),
543            Err(LinkSendError::DriverStopped)
544        ));
545        assert_eq!(
546            block_on(node.announce(&announce_dest, &identity, None)),
547            Err(LinkSendError::DriverStopped)
548        );
549    }
550
551    #[test]
552    fn per_link_counts_cover_failure_shutdown_and_receipt_cancellation() {
553        let pool = SendPool::new(3);
554        let waiting = pool.track([1; 16], true);
555        let tracking = pool.track([2; 16], true);
556        let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
557        completion.admit();
558        assert_eq!(
559            pool.snapshot().get(&[1; 16]),
560            Some(&SendCounts {
561                pending: 0,
562                waiting: 1
563            })
564        );
565        assert_eq!(
566            pool.snapshot().get(&[2; 16]),
567            Some(&SendCounts {
568                pending: 1,
569                waiting: 0
570            })
571        );
572        drop(receipt);
573        assert_eq!(
574            pool.snapshot()[&[2; 16]].pending,
575            1,
576            "dropping a receipt does not cancel transmission"
577        );
578        completion.finish(Err(LinkSendError::WriteFailed("broken pipe".into())));
579        assert!(!pool.snapshot().contains_key(&[2; 16]));
580        drop(waiting);
581        assert!(pool.snapshot().is_empty());
582
583        let tracking = pool.track([4; 16], false);
584        let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
585        completion.admit();
586        drop(completion);
587        assert_eq!(receipt.wait(), Err(LinkSendError::Interrupted));
588        assert!(pool.snapshot().is_empty());
589
590        let tracking = pool.track([3; 16], false);
591        let (completion, receipt) = Completion::new_tracked(pool.try_acquire().unwrap(), tracking);
592        completion.admit();
593        let waiting = pool.track([3; 16], true);
594        pool.close();
595        assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
596        assert!(pool.snapshot().is_empty());
597        drop(waiting);
598        drop(completion);
599        assert!(pool.snapshot().is_empty());
600    }
601
602    #[test]
603    fn node_try_rejects_full_capacity_and_async_send_wakes_without_blocking() {
604        use crate::event::Event;
605        use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
606        struct WakeCount(AtomicUsize);
607        impl futures::task::ArcWake for WakeCount {
608            fn wake_by_ref(this: &Arc<Self>) {
609                this.0.fetch_add(1, Ordering::SeqCst);
610            }
611        }
612        let (tx, rx) = crate::event::channel_with_capacity(1);
613        let node = crate::RnsNode::from_parts(
614            tx.clone(),
615            std::thread::spawn(|| {}),
616            None,
617            Arc::new(AtomicU64::new(1000)),
618            Arc::new(std::sync::atomic::AtomicBool::new(true)),
619        );
620        let receipt = node.try_send_on_link([1; 16], vec![1], 0).unwrap();
621        assert_eq!(
622            tx.link_send_pool().snapshot()[&[1; 16]],
623            SendCounts {
624                pending: 1,
625                waiting: 0
626            }
627        );
628        let first = match rx.try_recv().unwrap() {
629            Event::SendLinkTracked { completion, .. } => completion,
630            _ => panic!("expected a tracked send"),
631        };
632        assert!(matches!(
633            node.try_send_on_link([1; 16], vec![2], 0),
634            Err(LinkSendError::QueueFull)
635        ));
636        let mut pending = Box::pin(node.send_on_link([1; 16], vec![2], 0));
637        let wakes = Arc::new(WakeCount(AtomicUsize::new(0)));
638        let waker = futures::task::waker(wakes.clone());
639        let mut cx = Context::from_waker(&waker);
640        assert!(pending.as_mut().poll(&mut cx).is_pending());
641        assert_eq!(
642            tx.link_send_pool().snapshot()[&[1; 16]],
643            SendCounts {
644                pending: 1,
645                waiting: 1
646            }
647        );
648        assert!(
649            rx.try_recv().is_err(),
650            "capacity wait must not enqueue prematurely"
651        );
652        first.finish(Ok(()));
653        receipt.wait().unwrap();
654        assert_eq!(
655            tx.link_send_pool().snapshot()[&[1; 16]],
656            SendCounts {
657                pending: 0,
658                waiting: 1
659            }
660        );
661        assert!(
662            wakes.0.load(Ordering::SeqCst) > 0,
663            "capacity release must wake the task"
664        );
665        assert!(pending.as_mut().poll(&mut cx).is_pending());
666        let second = match rx.try_recv().unwrap() {
667            Event::SendLinkTracked { completion, .. } => completion,
668            _ => panic!("expected a tracked send"),
669        };
670        assert_eq!(
671            tx.link_send_pool().snapshot()[&[1; 16]],
672            SendCounts {
673                pending: 1,
674                waiting: 0
675            }
676        );
677        second.finish(Ok(()));
678        assert!(tx.link_send_pool().snapshot().is_empty());
679        assert_eq!(pending.as_mut().poll(&mut cx), Poll::Ready(Ok(())));
680        drop(pending);
681
682        // A full *driver* event queue also makes try return immediately.
683        tx.try_send(Event::Tick).unwrap();
684        assert!(matches!(
685            node.try_send_on_link([1; 16], vec![3], 0),
686            Err(LinkSendError::QueueFull)
687        ));
688        let mut cancelled = Box::pin(node.send_on_link([1; 16], vec![3], 0));
689        assert!(cancelled.as_mut().poll(&mut cx).is_pending());
690        assert_eq!(
691            tx.link_send_pool().snapshot()[&[1; 16]],
692            SendCounts {
693                pending: 0,
694                waiting: 1
695            }
696        );
697        drop(cancelled);
698        assert!(tx.link_send_pool().snapshot().is_empty());
699        assert_eq!(tx.link_send_pool().in_flight(), 0);
700        assert!(matches!(rx.try_recv(), Ok(Event::Tick)));
701        assert!(
702            rx.try_recv().is_err(),
703            "cancelled admission must not send later"
704        );
705        drop(rx);
706        node.shutdown();
707    }
708
709    #[test]
710    fn capacity_waits_for_transmission_not_receipt_creation() {
711        let pool = SendPool::new(1);
712        let (completion, mut receipt) = Completion::new(pool.try_acquire().unwrap());
713        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
714        assert!((&mut receipt).now_or_never().is_none());
715        let mut waiting = Box::pin(pool.acquire());
716        assert!(waiting.as_mut().now_or_never().is_none());
717        completion.finish(Ok(()));
718        receipt.wait().unwrap();
719        let permit = block_on(waiting).unwrap();
720        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
721        drop(permit);
722        assert!(pool.try_acquire().is_ok());
723    }
724
725    #[test]
726    fn shutdown_resolves_in_flight_receipts_and_capacity_waiters() {
727        let pool = SendPool::new(1);
728        let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
729        let mut waiting = Box::pin(pool.acquire());
730        assert!(waiting.as_mut().now_or_never().is_none());
731        pool.close();
732        assert_eq!(receipt.wait(), Err(LinkSendError::DriverStopped));
733        assert!(matches!(
734            block_on(waiting),
735            Err(LinkSendError::DriverStopped)
736        ));
737        // A delayed worker cannot overwrite the shutdown result.
738        completion.finish(Ok(()));
739    }
740
741    #[test]
742    fn dropping_receipt_does_not_release_an_in_flight_slot() {
743        let pool = SendPool::new(1);
744        let (completion, receipt) = Completion::new(pool.try_acquire().unwrap());
745        drop(receipt);
746        assert!(matches!(pool.try_acquire(), Err(LinkSendError::QueueFull)));
747        drop(completion);
748        assert!(pool.try_acquire().is_ok());
749    }
750
751    #[test]
752    fn both_node_apis_report_the_same_invalid_link_error() {
753        struct Callbacks;
754        impl crate::Callbacks for Callbacks {
755            fn on_announce(&mut self, _: crate::AnnouncedIdentity) {}
756            fn on_path_updated(&mut self, _: crate::DestHash, _: u8) {}
757            fn on_local_delivery(&mut self, _: crate::DestHash, _: Vec<u8>, _: crate::PacketHash) {}
758        }
759        let node =
760            crate::RnsNode::start(crate::NodeConfig::default(), Box::new(Callbacks)).unwrap();
761        let expected = Err(LinkSendError::InvalidPacket(
762            crate::event::LinkDatagramError::LinkNotFound,
763        ));
764        assert_eq!(
765            block_on(node.send_on_link([0x42; 16], vec![1], 0)),
766            expected
767        );
768        assert_eq!(
769            node.try_send_on_link([0x42; 16], vec![1], 0)
770                .unwrap()
771                .wait(),
772            expected
773        );
774        let dest = crate::Destination::plain("confirmed", &["no_route"]);
775        assert_eq!(
776            block_on(node.send_packet(&dest, b"data")),
777            Err(LinkSendError::NoRoute)
778        );
779        assert_eq!(
780            node.try_send_packet(&dest, b"data").unwrap().wait(),
781            Err(LinkSendError::NoRoute)
782        );
783        let identity = rns_crypto::identity::Identity::new(&mut rns_crypto::OsRng);
784        let announce_dest = crate::Destination::single_in(
785            "confirmed",
786            &["announce"],
787            crate::IdentityHash(*identity.hash()),
788        );
789        assert_eq!(
790            block_on(node.announce(&announce_dest, &identity, None)),
791            Err(LinkSendError::NoRoute)
792        );
793        assert_eq!(
794            node.try_announce(&announce_dest, &identity, None)
795                .unwrap()
796                .wait(),
797            Err(LinkSendError::NoRoute)
798        );
799        let oversized = vec![0; rns_core::constants::MTU * 2];
800        assert_eq!(
801            block_on(node.send_packet(&dest, &oversized)),
802            Err(LinkSendError::PacketBuildFailed)
803        );
804        assert!(matches!(
805            node.try_send_packet(&dest, &oversized),
806            Err(LinkSendError::PacketBuildFailed)
807        ));
808        node.begin_drain(std::time::Duration::from_secs(30))
809            .unwrap();
810        node.drain_status().unwrap();
811        assert_eq!(
812            block_on(node.send_packet(&dest, b"data")),
813            Err(LinkSendError::Draining)
814        );
815        assert_eq!(
816            node.try_announce(&announce_dest, &identity, None)
817                .unwrap()
818                .wait(),
819            Err(LinkSendError::Draining)
820        );
821        node.shutdown();
822    }
823}