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