1use crate::config::{EthernetConfig, NostrDiscoveryPolicy, TransportInstances, UdpConfig};
7#[cfg(test)]
8use crate::node::ENDPOINT_EVENT_TEST_PAYLOAD_LEN;
9use crate::node::{
10 EndpointDataBatchTx, EndpointDataPayload, EndpointDirectSink, EndpointEventSender,
11 EndpointServiceEventSender, NodeEndpointControlCommand, NodeEndpointDataBatch,
12 NodeEndpointEvent,
13};
14use crate::upper::tun::TunOutboundTx;
15use crate::{
16 Config, FipsAddress, IdentityConfig, Node, NodeAddr, NodeDeliveredPacket, NodeError,
17 PeerIdentity,
18};
19use std::collections::HashMap;
20use std::sync::{Arc, Mutex as StdMutex};
21use thiserror::Error;
22use tokio::sync::{Mutex, mpsc, oneshot};
23use tokio::task::JoinHandle;
24
25const ENDPOINT_DATA_BATCH_MAX: usize = 128;
26const ENDPOINT_RECV_BATCH_MAX: usize = 128;
27
28mod builder;
29mod receive;
30mod status;
31
32#[cfg(test)]
33mod tests;
34
35pub use crate::node::{
36 FipsEndpointDirectDeliveryError, FipsEndpointDirectPacketBatch, FipsEndpointDirectPacketRun,
37 FipsEndpointDirectSink,
38};
39pub use builder::FipsEndpointBuilder;
40use receive::{EndpointReceiveState, ServiceReceiveState};
41pub use status::{FipsEndpointPeer, FipsEndpointRelayStatus};
42
43pub type FipsEndpointData = crate::transport::PacketBuffer;
48
49#[derive(Debug, Error)]
51pub enum FipsEndpointError {
52 #[error("node error: {0}")]
53 Node(#[from] NodeError),
54
55 #[error("endpoint task failed: {0}")]
56 TaskJoin(#[from] tokio::task::JoinError),
57
58 #[error("endpoint is closed")]
59 Closed,
60
61 #[error("endpoint data payload is too large: {len} bytes exceeds max {max} bytes")]
62 EndpointDataTooLarge { len: usize, max: usize },
63
64 #[error("service datagram payload is too large: {len} bytes exceeds max {max} bytes")]
65 ServiceDatagramTooLarge { len: usize, max: usize },
66
67 #[error("FSP service port {port} is reserved")]
68 ServicePortReserved { port: u16 },
69
70 #[error("FSP service port {port} is already registered")]
71 ServicePortAlreadyRegistered { port: u16 },
72}
73
74#[derive(Debug, Clone, PartialEq, Eq)]
76pub struct FipsEndpointMessage {
77 pub source_peer: PeerIdentity,
79 pub data: FipsEndpointData,
81 pub enqueued_at_ms: u64,
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct FipsEndpointOutboundDatagram {
88 pub source_port: u16,
89 pub destination_port: u16,
90 pub data: Vec<u8>,
91}
92
93impl FipsEndpointOutboundDatagram {
94 pub fn new(source_port: u16, destination_port: u16, data: Vec<u8>) -> Self {
95 Self {
96 source_port,
97 destination_port,
98 data,
99 }
100 }
101}
102
103#[derive(Debug, Clone, PartialEq, Eq)]
105pub struct FipsEndpointServiceDatagram {
106 pub source_peer: PeerIdentity,
107 pub source_port: u16,
108 pub destination_port: u16,
109 pub data: FipsEndpointData,
110 pub enqueued_at_ms: u64,
111}
112
113pub struct FipsEndpointServiceReceiver {
118 state: Mutex<ServiceReceiveState>,
119}
120
121impl FipsEndpointServiceReceiver {
122 pub async fn recv_batch_into(
124 &self,
125 datagrams: &mut Vec<FipsEndpointServiceDatagram>,
126 max: usize,
127 ) -> Option<usize> {
128 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
129 datagrams.clear();
130
131 let mut state = self.state.lock().await;
132 state.drain_pending_into(datagrams, max);
133 while datagrams.len() < max {
134 let event = if datagrams.is_empty() {
135 state.rx.recv().await?
136 } else {
137 match state.rx.try_recv() {
138 Ok(event) => event,
139 Err(_) => break,
140 }
141 };
142 state.push_event_into(event, datagrams, max);
143 }
144 Some(datagrams.len())
145 }
146}
147
148#[derive(Debug, Clone, Default, PartialEq, Eq)]
150pub struct UpdatePeersOutcome {
151 pub added: usize,
154 pub removed: usize,
158 pub updated: usize,
163 pub unchanged: usize,
165}
166
167impl From<crate::node::UpdatePeersOutcome> for UpdatePeersOutcome {
168 fn from(value: crate::node::UpdatePeersOutcome) -> Self {
169 Self {
170 added: value.added,
171 removed: value.removed,
172 updated: value.updated,
173 unchanged: value.unchanged,
174 }
175 }
176}
177
178fn apply_default_scoped_discovery(config: &mut Config, scope: &str) {
179 if config.node.discovery.nostr.enabled || !config.transports.is_empty() {
180 return;
181 }
182
183 config.node.discovery.nostr.enabled = true;
184 config.node.discovery.nostr.advertise = true;
185 config.node.discovery.nostr.policy = NostrDiscoveryPolicy::Open;
186 config.node.discovery.nostr.share_local_candidates = true;
187 config.node.discovery.nostr.app = scope.to_string();
188 config.node.discovery.lan.scope = Some(scope.to_string());
189 config.node.discovery.local.enabled = true;
190 config.transports.udp = TransportInstances::Single(UdpConfig {
191 bind_addr: Some("0.0.0.0:0".to_string()),
192 advertise_on_nostr: Some(true),
193 public: Some(false),
194 outbound_only: Some(false),
195 accept_connections: Some(true),
196 ..UdpConfig::default()
197 });
198}
199
200fn endpoint_ethernet_config(interface: &str, scope: Option<&str>) -> EthernetConfig {
201 EthernetConfig {
202 interface: interface.to_string(),
203 discovery: Some(true),
204 announce: Some(true),
205 auto_connect: Some(true),
206 accept_connections: Some(true),
207 discovery_scope: scope
208 .map(str::trim)
209 .filter(|s| !s.is_empty())
210 .map(str::to_string),
211 ..EthernetConfig::default()
212 }
213}
214
215fn add_endpoint_ethernet_transport(config: &mut Config, interface: &str, scope: Option<&str>) {
216 let eth = endpoint_ethernet_config(interface, scope);
217 if config.transports.ethernet.is_empty() {
218 config.transports.ethernet = TransportInstances::Single(eth);
219 return;
220 }
221
222 let existing = std::mem::take(&mut config.transports.ethernet);
223 let mut named = match existing {
224 TransportInstances::Single(config) => {
225 let mut map = std::collections::HashMap::new();
226 map.insert("default".to_string(), config);
227 map
228 }
229 TransportInstances::Named(map) => map,
230 };
231
232 let base_name = endpoint_ethernet_instance_name(interface);
233 let mut name = base_name.clone();
234 let mut suffix = 2usize;
235 while named.contains_key(&name) {
236 name = format!("{base_name}-{suffix}");
237 suffix += 1;
238 }
239 named.insert(name, eth);
240 config.transports.ethernet = TransportInstances::Named(named);
241}
242
243fn endpoint_ethernet_instance_name(interface: &str) -> String {
244 let suffix: String = interface
245 .chars()
246 .map(|c| {
247 if c.is_ascii_alphanumeric() {
248 c.to_ascii_lowercase()
249 } else {
250 '-'
251 }
252 })
253 .collect();
254 let suffix = suffix.trim_matches('-');
255 if suffix.is_empty() {
256 "local-ethernet".to_string()
257 } else {
258 format!("local-ethernet-{suffix}")
259 }
260}
261
262fn endpoint_data_payloads_from_vecs(
263 payloads: Vec<Vec<u8>>,
264) -> Result<Vec<EndpointDataPayload>, FipsEndpointError> {
265 let mut converted = Vec::with_capacity(payloads.len());
266 for payload in payloads {
267 let len = payload.len();
268 let Some(payload) = EndpointDataPayload::from_packet_payload(payload) else {
269 let max = crate::node::session_wire::fsp_endpoint_data_max_body_len();
270 return Err(FipsEndpointError::EndpointDataTooLarge { len, max });
271 };
272 converted.push(payload);
273 }
274 Ok(converted)
275}
276
277fn service_datagram_payloads(
278 datagrams: Vec<FipsEndpointOutboundDatagram>,
279) -> Result<Vec<EndpointDataPayload>, FipsEndpointError> {
280 let max = crate::node::session_wire::fsp_service_datagram_max_body_len();
281 let mut payloads = Vec::with_capacity(datagrams.len());
282 for datagram in datagrams {
283 let len = datagram.data.len();
284 let Some(payload) = EndpointDataPayload::from_service_datagram(
285 datagram.source_port,
286 datagram.destination_port,
287 datagram.data,
288 ) else {
289 return Err(FipsEndpointError::ServiceDatagramTooLarge { len, max });
290 };
291 payloads.push(payload);
292 }
293 Ok(payloads)
294}
295
296fn spawn_node_task(
297 mut node: Node,
298 shutdown_rx: oneshot::Receiver<()>,
299) -> JoinHandle<Result<(), NodeError>> {
300 tokio::spawn(async move {
301 tokio::pin!(shutdown_rx);
302 let loop_result = tokio::select! {
303 result = node.run_rx_loop() => result,
304 _ = &mut shutdown_rx => Ok(()),
305 };
306 let stop_result = if node.state().can_stop() {
307 node.stop().await
308 } else {
309 Ok(())
310 };
311 loop_result?;
312 stop_result
313 })
314}
315
316pub struct FipsEndpoint {
318 identity: PeerIdentity,
319 npub: String,
320 node_addr: NodeAddr,
321 address: FipsAddress,
322 discovery_scope: Option<String>,
323 outbound_packets: TunOutboundTx,
324 delivered_packets: Arc<Mutex<mpsc::Receiver<NodeDeliveredPacket>>>,
325 endpoint_control_tx: mpsc::Sender<NodeEndpointControlCommand>,
326 endpoint_data_batches: EndpointDataBatchTx,
327 inbound_endpoint_tx: EndpointEventSender,
333 inbound_endpoint_rx: Arc<Mutex<EndpointReceiveState>>,
340 inbound_service_tx: EndpointServiceEventSender,
341 inbound_service_rx: Arc<Mutex<ServiceReceiveState>>,
342 registered_services: Arc<StdMutex<HashMap<u16, EndpointServiceEventSender>>>,
343 service_channel_capacity: usize,
344 shutdown_tx: StdMutex<Option<oneshot::Sender<()>>>,
345 task: StdMutex<Option<JoinHandle<Result<(), NodeError>>>>,
346}
347
348impl FipsEndpoint {
349 pub fn builder() -> FipsEndpointBuilder {
351 FipsEndpointBuilder::default()
352 }
353
354 pub fn npub(&self) -> &str {
356 &self.npub
357 }
358
359 pub fn node_addr(&self) -> &NodeAddr {
361 &self.node_addr
362 }
363
364 pub fn address(&self) -> FipsAddress {
366 self.address
367 }
368
369 pub fn discovery_scope(&self) -> Option<&str> {
371 self.discovery_scope.as_deref()
372 }
373
374 pub async fn send_batch_to_peer(
382 &self,
383 remote: PeerIdentity,
384 payloads: Vec<Vec<u8>>,
385 ) -> Result<(), FipsEndpointError> {
386 self.send_payloads_to_peer(remote, payloads)
387 }
388
389 pub async fn register_service(&self, port: u16) -> Result<(), FipsEndpointError> {
394 self.register_service_with_sender(port, self.inbound_service_tx.clone())
395 .await
396 }
397
398 pub async fn register_service_receiver(
400 &self,
401 port: u16,
402 ) -> Result<FipsEndpointServiceReceiver, FipsEndpointError> {
403 let (sender, receiver) = EndpointServiceEventSender::channel(self.service_channel_capacity);
404 self.register_service_with_sender(port, sender).await?;
405 Ok(FipsEndpointServiceReceiver {
406 state: Mutex::new(ServiceReceiveState::new(receiver)),
407 })
408 }
409
410 async fn register_service_with_sender(
411 &self,
412 port: u16,
413 sender: EndpointServiceEventSender,
414 ) -> Result<(), FipsEndpointError> {
415 if port == crate::node::session_wire::FSP_PORT_IPV6_SHIM {
416 return Err(FipsEndpointError::ServicePortReserved { port });
417 }
418
419 let (response_tx, response_rx) = oneshot::channel();
420 self.endpoint_control_tx
421 .send(NodeEndpointControlCommand::RegisterService {
422 port,
423 sender: sender.clone(),
424 response_tx,
425 })
426 .await
427 .map_err(|_| FipsEndpointError::Closed)?;
428 if !response_rx.await.map_err(|_| FipsEndpointError::Closed)? {
429 return Err(FipsEndpointError::ServicePortAlreadyRegistered { port });
430 }
431 self.registered_services
432 .lock()
433 .map_err(|_| FipsEndpointError::Closed)?
434 .insert(port, sender);
435 Ok(())
436 }
437
438 pub async fn send_datagram(
440 &self,
441 remote: PeerIdentity,
442 source_port: u16,
443 destination_port: u16,
444 payload: Vec<u8>,
445 ) -> Result<(), FipsEndpointError> {
446 self.send_service_datagrams_to_peer(
447 remote,
448 vec![FipsEndpointOutboundDatagram::new(
449 source_port,
450 destination_port,
451 payload,
452 )],
453 )
454 }
455
456 pub async fn send_datagram_batch_to_peer(
458 &self,
459 remote: PeerIdentity,
460 datagrams: Vec<FipsEndpointOutboundDatagram>,
461 ) -> Result<(), FipsEndpointError> {
462 self.send_service_datagrams_to_peer(remote, datagrams)
463 }
464
465 fn send_service_datagrams_to_peer(
466 &self,
467 remote: PeerIdentity,
468 datagrams: Vec<FipsEndpointOutboundDatagram>,
469 ) -> Result<(), FipsEndpointError> {
470 let max = crate::node::session_wire::fsp_service_datagram_max_body_len();
471 if let Some(datagram) = datagrams.iter().find(|datagram| datagram.data.len() > max) {
472 return Err(FipsEndpointError::ServiceDatagramTooLarge {
473 len: datagram.data.len(),
474 max,
475 });
476 }
477 if datagrams.is_empty() {
478 return Ok(());
479 }
480
481 if *remote.node_addr() == self.node_addr {
482 let deliveries_by_port = {
483 let registered = self
484 .registered_services
485 .lock()
486 .map_err(|_| FipsEndpointError::Closed)?;
487 let mut grouped: HashMap<
488 u16,
489 (
490 EndpointServiceEventSender,
491 Vec<crate::node::EndpointServiceDatagramDelivery>,
492 ),
493 > = HashMap::new();
494 for datagram in datagrams {
495 let Some(sender) = registered.get(&datagram.destination_port) else {
496 continue;
497 };
498 grouped
499 .entry(datagram.destination_port)
500 .or_insert_with(|| (sender.clone(), Vec::new()))
501 .1
502 .push(crate::node::EndpointServiceDatagramDelivery::new(
503 self.identity,
504 datagram.source_port,
505 datagram.destination_port,
506 crate::transport::PacketBuffer::new(datagram.data),
507 ));
508 }
509 grouped
510 };
511 for (_, (sender, deliveries)) in deliveries_by_port {
512 sender
513 .send(deliveries)
514 .map_err(|_| FipsEndpointError::Closed)?;
515 }
516 return Ok(());
517 }
518
519 self.send_endpoint_data_batch(remote, service_datagram_payloads(datagrams)?)
520 }
521
522 fn send_payloads_to_peer(
523 &self,
524 remote: PeerIdentity,
525 payloads: Vec<Vec<u8>>,
526 ) -> Result<(), FipsEndpointError> {
527 let payloads = endpoint_data_payloads_from_vecs(payloads)?;
528 if *remote.node_addr() == self.node_addr {
529 for payload in payloads {
530 self.send_loopback(payload)?;
531 }
532 return Ok(());
533 }
534
535 self.send_endpoint_data_batch(remote, payloads)
536 }
537
538 fn send_endpoint_data_batch(
539 &self,
540 remote: PeerIdentity,
541 payloads: Vec<EndpointDataPayload>,
542 ) -> Result<(), FipsEndpointError> {
543 if payloads.is_empty() {
544 return Ok(());
545 }
546
547 if payloads.len() <= ENDPOINT_DATA_BATCH_MAX {
548 self.enqueue_endpoint_data_batch(remote, payloads)?;
549 return Ok(());
550 }
551
552 let mut payloads = payloads.into_iter();
553 loop {
554 let payload_batch: Vec<_> = payloads.by_ref().take(ENDPOINT_DATA_BATCH_MAX).collect();
555 if payload_batch.is_empty() {
556 break;
557 }
558 self.enqueue_endpoint_data_batch(remote, payload_batch)?;
559 }
560 Ok(())
561 }
562
563 fn enqueue_endpoint_data_batch(
564 &self,
565 remote: PeerIdentity,
566 payload_batch: Vec<EndpointDataPayload>,
567 ) -> Result<(), FipsEndpointError> {
568 if let Some(batch) = NodeEndpointDataBatch::from_payloads(
573 remote,
574 payload_batch,
575 crate::perf_profile::stamp(),
576 ) {
577 self.endpoint_data_batches
578 .send_or_drop(batch)
579 .map_err(|_| FipsEndpointError::Closed)?;
580 }
581 Ok(())
582 }
583
584 fn send_loopback(&self, payload: EndpointDataPayload) -> Result<(), FipsEndpointError> {
585 self.inbound_endpoint_tx
586 .send(NodeEndpointEvent {
587 messages: vec![crate::node::EndpointDataDelivery::new(
588 self.identity,
589 payload.into_body(),
590 )],
591 queued_at: crate::perf_profile::stamp(),
592 })
593 .map_err(|_| FipsEndpointError::Closed)
594 }
595
596 pub async fn recv_batch_into(
603 &self,
604 messages: &mut Vec<FipsEndpointMessage>,
605 max: usize,
606 ) -> Option<usize> {
607 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
608 messages.clear();
609
610 let mut state = self.inbound_endpoint_rx.lock().await;
611 state.drain_pending_into(messages, max);
612
613 while messages.len() < max {
614 let event = if messages.is_empty() {
615 state.rx.recv().await?
616 } else {
617 match state.rx.try_recv() {
618 Ok(event) => event,
619 Err(_) => break,
620 }
621 };
622 state.push_event_into(event, messages, max);
623 }
624
625 Some(messages.len())
626 }
627
628 pub async fn recv_service_datagram_batch_into(
630 &self,
631 datagrams: &mut Vec<FipsEndpointServiceDatagram>,
632 max: usize,
633 ) -> Option<usize> {
634 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
635 datagrams.clear();
636
637 let mut state = self.inbound_service_rx.lock().await;
638 state.drain_pending_into(datagrams, max);
639 while datagrams.len() < max {
640 let event = if datagrams.is_empty() {
641 state.rx.recv().await?
642 } else {
643 match state.rx.try_recv() {
644 Ok(event) => event,
645 Err(_) => break,
646 }
647 };
648 state.push_event_into(event, datagrams, max);
649 }
650 Some(datagrams.len())
651 }
652
653 pub fn blocking_send_batch_to_peer(
659 &self,
660 remote: PeerIdentity,
661 payloads: Vec<Vec<u8>>,
662 ) -> Result<(), FipsEndpointError> {
663 self.send_payloads_to_peer(remote, payloads)
664 }
665
666 pub fn blocking_send_datagram(
668 &self,
669 remote: PeerIdentity,
670 source_port: u16,
671 destination_port: u16,
672 payload: Vec<u8>,
673 ) -> Result<(), FipsEndpointError> {
674 self.send_service_datagrams_to_peer(
675 remote,
676 vec![FipsEndpointOutboundDatagram::new(
677 source_port,
678 destination_port,
679 payload,
680 )],
681 )
682 }
683
684 pub fn blocking_send_datagram_batch_to_peer(
686 &self,
687 remote: PeerIdentity,
688 datagrams: Vec<FipsEndpointOutboundDatagram>,
689 ) -> Result<(), FipsEndpointError> {
690 self.send_service_datagrams_to_peer(remote, datagrams)
691 }
692
693 pub fn blocking_recv_batch_into(
701 &self,
702 messages: &mut Vec<FipsEndpointMessage>,
703 max: usize,
704 ) -> Option<usize> {
705 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
706 messages.clear();
707
708 let mut state = self.inbound_endpoint_rx.blocking_lock();
709 state.drain_pending_into(messages, max);
710
711 while messages.len() < max {
712 let event = if messages.is_empty() {
713 state.rx.blocking_recv()?
714 } else {
715 match state.rx.try_recv() {
716 Ok(event) => event,
717 Err(_) => break,
718 }
719 };
720 state.push_event_into(event, messages, max);
721 }
722
723 Some(messages.len())
724 }
725
726 pub fn blocking_recv_service_datagram_batch_into(
728 &self,
729 datagrams: &mut Vec<FipsEndpointServiceDatagram>,
730 max: usize,
731 ) -> Option<usize> {
732 let max = max.clamp(1, ENDPOINT_RECV_BATCH_MAX);
733 datagrams.clear();
734
735 let mut state = self.inbound_service_rx.blocking_lock();
736 state.drain_pending_into(datagrams, max);
737 while datagrams.len() < max {
738 let event = if datagrams.is_empty() {
739 state.rx.blocking_recv()?
740 } else {
741 match state.rx.try_recv() {
742 Ok(event) => event,
743 Err(_) => break,
744 }
745 };
746 state.push_event_into(event, datagrams, max);
747 }
748 Some(datagrams.len())
749 }
750
751 pub async fn update_peers(
761 &self,
762 peers: Vec<crate::config::PeerConfig>,
763 ) -> Result<UpdatePeersOutcome, FipsEndpointError> {
764 let (response_tx, response_rx) = oneshot::channel();
765 self.endpoint_control_tx
766 .send(NodeEndpointControlCommand::UpdatePeers { peers, response_tx })
767 .await
768 .map_err(|_| FipsEndpointError::Closed)?;
769
770 match response_rx.await.map_err(|_| FipsEndpointError::Closed)? {
771 Ok(outcome) => Ok(UpdatePeersOutcome::from(outcome)),
772 Err(error) => Err(FipsEndpointError::Node(error)),
773 }
774 }
775
776 pub async fn refresh_peer_paths(
782 &self,
783 peers: Vec<PeerIdentity>,
784 ) -> Result<usize, FipsEndpointError> {
785 let (response_tx, response_rx) = oneshot::channel();
786 let npubs = peers.into_iter().map(|peer| peer.npub()).collect();
787 self.endpoint_control_tx
788 .send(NodeEndpointControlCommand::RefreshPeerPaths { npubs, response_tx })
789 .await
790 .map_err(|_| FipsEndpointError::Closed)?;
791
792 match response_rx.await.map_err(|_| FipsEndpointError::Closed)? {
793 Ok(refreshed) => Ok(refreshed),
794 Err(error) => Err(FipsEndpointError::Node(error)),
795 }
796 }
797
798 pub async fn peers(&self) -> Result<Vec<FipsEndpointPeer>, FipsEndpointError> {
800 let (response_tx, response_rx) = oneshot::channel();
801 self.endpoint_control_tx
802 .send(NodeEndpointControlCommand::PeerSnapshot { response_tx })
803 .await
804 .map_err(|_| FipsEndpointError::Closed)?;
805
806 response_rx
807 .await
808 .map(|peers| peers.into_iter().map(FipsEndpointPeer::from).collect())
809 .map_err(|_| FipsEndpointError::Closed)
810 }
811
812 pub async fn peer_rating_events(
815 &self,
816 scope: impl Into<String>,
817 ) -> Result<Vec<nostr::Event>, FipsEndpointError> {
818 let (response_tx, response_rx) = oneshot::channel();
819 self.endpoint_control_tx
820 .send(NodeEndpointControlCommand::PeerRatingEvents {
821 scope: scope.into(),
822 response_tx,
823 })
824 .await
825 .map_err(|_| FipsEndpointError::Closed)?;
826 response_rx
827 .await
828 .map_err(|_| FipsEndpointError::Closed)?
829 .map_err(FipsEndpointError::Node)
830 }
831
832 pub async fn ingest_nostr_pubsub_event(
840 &self,
841 event: nostr::Event,
842 ) -> Result<bool, FipsEndpointError> {
843 let (response_tx, response_rx) = oneshot::channel();
844 self.endpoint_control_tx
845 .send(NodeEndpointControlCommand::IngestNostrPubsubEvent { event, response_tx })
846 .await
847 .map_err(|_| FipsEndpointError::Closed)?;
848 response_rx.await.map_err(|_| FipsEndpointError::Closed)
849 }
850
851 pub async fn local_advertised_endpoints(
854 &self,
855 ) -> Result<Vec<crate::discovery::nostr::OverlayEndpointAdvert>, FipsEndpointError> {
856 let (response_tx, response_rx) = oneshot::channel();
857 self.endpoint_control_tx
858 .send(NodeEndpointControlCommand::LocalAdvertSnapshot { response_tx })
859 .await
860 .map_err(|_| FipsEndpointError::Closed)?;
861
862 response_rx.await.map_err(|_| FipsEndpointError::Closed)
863 }
864
865 pub async fn relay_statuses(&self) -> Result<Vec<FipsEndpointRelayStatus>, FipsEndpointError> {
867 let (response_tx, response_rx) = oneshot::channel();
868 self.endpoint_control_tx
869 .send(NodeEndpointControlCommand::RelaySnapshot { response_tx })
870 .await
871 .map_err(|_| FipsEndpointError::Closed)?;
872
873 response_rx
874 .await
875 .map(|relays| {
876 relays
877 .into_iter()
878 .map(FipsEndpointRelayStatus::from)
879 .collect()
880 })
881 .map_err(|_| FipsEndpointError::Closed)
882 }
883
884 pub async fn update_relays(
886 &self,
887 advert_relays: Vec<String>,
888 dm_relays: Vec<String>,
889 ) -> Result<(), FipsEndpointError> {
890 let (response_tx, response_rx) = oneshot::channel();
891 self.endpoint_control_tx
892 .send(NodeEndpointControlCommand::UpdateRelays {
893 advert_relays,
894 dm_relays,
895 response_tx,
896 })
897 .await
898 .map_err(|_| FipsEndpointError::Closed)?;
899
900 response_rx
901 .await
902 .map_err(|_| FipsEndpointError::Closed)?
903 .map_err(FipsEndpointError::Node)
904 }
905
906 pub async fn send_ip_packet(
908 &self,
909 packet: impl Into<Vec<u8>>,
910 ) -> Result<(), FipsEndpointError> {
911 self.outbound_packets
912 .send(packet.into())
913 .await
914 .map_err(|_| FipsEndpointError::Closed)
915 }
916
917 pub async fn recv_ip_packet(&self) -> Option<NodeDeliveredPacket> {
919 self.delivered_packets.lock().await.recv().await
920 }
921
922 pub async fn shutdown(&self) -> Result<(), FipsEndpointError> {
924 let shutdown_tx = self
925 .shutdown_tx
926 .lock()
927 .map_err(|_| FipsEndpointError::Closed)?
928 .take();
929 if let Some(shutdown_tx) = shutdown_tx {
930 let _ = shutdown_tx.send(());
931 }
932 let task = self
933 .task
934 .lock()
935 .map_err(|_| FipsEndpointError::Closed)?
936 .take();
937 if let Some(task) = task {
938 task.await??;
939 }
940 Ok(())
941 }
942}
943
944impl Drop for FipsEndpoint {
945 fn drop(&mut self) {
946 if let Ok(mut shutdown_tx) = self.shutdown_tx.lock()
947 && let Some(shutdown_tx) = shutdown_tx.take()
948 {
949 let _ = shutdown_tx.send(());
950 }
951 if let Ok(mut task) = self.task.lock()
952 && let Some(task) = task.take()
953 {
954 task.abort();
955 }
956 }
957}