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 std::time::Duration;
22use thiserror::Error;
23use tokio::sync::{Mutex, mpsc, oneshot};
24use tokio::task::JoinHandle;
25
26const ENDPOINT_DATA_BATCH_MAX: usize = 128;
27const ENDPOINT_RECV_BATCH_MAX: usize = 128;
28const ENDPOINT_OPERATION_TIMEOUT: Duration = Duration::from_secs(5);
29
30mod builder;
31mod receive;
32mod service_receiver;
33mod status;
34
35#[cfg(test)]
36mod tests;
37
38pub use crate::node::{
39 FIPS_ENDPOINT_DIRECT_PACKET_QUEUE_MAX_PACKETS, FIPS_ENDPOINT_DIRECT_PACKET_RUN_MAX_PACKETS,
40 FipsEndpointDirectDeliveryError, FipsEndpointDirectPacketBatch, FipsEndpointDirectPacketRun,
41 FipsEndpointDirectReceiver, FipsEndpointDirectSink,
42};
43pub use builder::FipsEndpointBuilder;
44use receive::{EndpointReceiveState, ServiceReceiveState};
45pub use status::{FipsEndpointPeer, FipsEndpointRelayStatus};
46
47pub type FipsEndpointData = crate::transport::PacketBuffer;
52
53#[derive(Debug, Error)]
55pub enum FipsEndpointError {
56 #[error("node error: {0}")]
57 Node(#[from] NodeError),
58
59 #[error("endpoint task failed: {0}")]
60 TaskJoin(#[from] tokio::task::JoinError),
61
62 #[error("endpoint is closed")]
63 Closed,
64
65 #[error("endpoint {operation} timed out")]
66 Timeout { operation: &'static str },
67
68 #[error("endpoint data payload is too large: {len} bytes exceeds max {max} bytes")]
69 EndpointDataTooLarge { len: usize, max: usize },
70
71 #[error("service datagram payload is too large: {len} bytes exceeds max {max} bytes")]
72 ServiceDatagramTooLarge { len: usize, max: usize },
73
74 #[error("FSP service port {port} is reserved")]
75 ServicePortReserved { port: u16 },
76
77 #[error("FSP service port {port} is already registered")]
78 ServicePortAlreadyRegistered { port: u16 },
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
83pub struct FipsEndpointMessage {
84 pub source_peer: PeerIdentity,
86 pub data: FipsEndpointData,
88 pub enqueued_at_ms: u64,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct FipsEndpointOutboundDatagram {
95 pub source_port: u16,
96 pub destination_port: u16,
97 pub data: Vec<u8>,
98}
99
100impl FipsEndpointOutboundDatagram {
101 pub fn new(source_port: u16, destination_port: u16, data: Vec<u8>) -> Self {
102 Self {
103 source_port,
104 destination_port,
105 data,
106 }
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct FipsEndpointServiceDatagram {
113 pub source_peer: PeerIdentity,
114 pub source_port: u16,
115 pub destination_port: u16,
116 pub data: FipsEndpointData,
117 pub enqueued_at_ms: u64,
118}
119
120pub struct FipsEndpointServiceReceiver {
125 state: Mutex<ServiceReceiveState>,
126}
127
128#[derive(Debug, Clone, Default, PartialEq, Eq)]
130pub struct UpdatePeersOutcome {
131 pub added: usize,
134 pub removed: usize,
138 pub updated: usize,
143 pub unchanged: usize,
145}
146
147impl From<crate::node::UpdatePeersOutcome> for UpdatePeersOutcome {
148 fn from(value: crate::node::UpdatePeersOutcome) -> Self {
149 Self {
150 added: value.added,
151 removed: value.removed,
152 updated: value.updated,
153 unchanged: value.unchanged,
154 }
155 }
156}
157
158fn apply_default_scoped_discovery(config: &mut Config, scope: &str) {
159 if config.node.discovery.nostr.enabled || !config.transports.is_empty() {
160 return;
161 }
162
163 config.node.discovery.nostr.enabled = true;
164 config.node.discovery.nostr.advertise = true;
165 config.node.discovery.nostr.policy = NostrDiscoveryPolicy::Open;
166 config.node.discovery.nostr.share_local_candidates = true;
167 config.node.discovery.nostr.app = scope.to_string();
168 config.node.discovery.lan.scope = Some(scope.to_string());
169 config.node.discovery.local.enabled = true;
170 config.transports.udp = TransportInstances::Single(UdpConfig {
171 bind_addr: Some("0.0.0.0:0".to_string()),
172 advertise_on_nostr: Some(true),
173 public: Some(false),
174 outbound_only: Some(false),
175 accept_connections: Some(true),
176 ..UdpConfig::default()
177 });
178}
179
180fn endpoint_ethernet_config(interface: &str, scope: Option<&str>) -> EthernetConfig {
181 EthernetConfig {
182 interface: interface.to_string(),
183 discovery: Some(true),
184 announce: Some(true),
185 auto_connect: Some(true),
186 accept_connections: Some(true),
187 discovery_scope: scope
188 .map(str::trim)
189 .filter(|s| !s.is_empty())
190 .map(str::to_string),
191 ..EthernetConfig::default()
192 }
193}
194
195fn add_endpoint_ethernet_transport(config: &mut Config, interface: &str, scope: Option<&str>) {
196 let eth = endpoint_ethernet_config(interface, scope);
197 if config.transports.ethernet.is_empty() {
198 config.transports.ethernet = TransportInstances::Single(eth);
199 return;
200 }
201
202 let existing = std::mem::take(&mut config.transports.ethernet);
203 let mut named = match existing {
204 TransportInstances::Single(config) => {
205 let mut map = std::collections::HashMap::new();
206 map.insert("default".to_string(), config);
207 map
208 }
209 TransportInstances::Named(map) => map,
210 };
211
212 let base_name = endpoint_ethernet_instance_name(interface);
213 let mut name = base_name.clone();
214 let mut suffix = 2usize;
215 while named.contains_key(&name) {
216 name = format!("{base_name}-{suffix}");
217 suffix += 1;
218 }
219 named.insert(name, eth);
220 config.transports.ethernet = TransportInstances::Named(named);
221}
222
223fn endpoint_ethernet_instance_name(interface: &str) -> String {
224 let suffix: String = interface
225 .chars()
226 .map(|c| {
227 if c.is_ascii_alphanumeric() {
228 c.to_ascii_lowercase()
229 } else {
230 '-'
231 }
232 })
233 .collect();
234 let suffix = suffix.trim_matches('-');
235 if suffix.is_empty() {
236 "local-ethernet".to_string()
237 } else {
238 format!("local-ethernet-{suffix}")
239 }
240}
241
242fn endpoint_data_payloads_from_vecs(
243 payloads: Vec<Vec<u8>>,
244) -> Result<Vec<EndpointDataPayload>, FipsEndpointError> {
245 let mut converted = Vec::with_capacity(payloads.len());
246 for payload in payloads {
247 let len = payload.len();
248 let Some(payload) = EndpointDataPayload::from_packet_payload(payload) else {
249 let max = crate::node::session_wire::fsp_endpoint_data_max_body_len();
250 return Err(FipsEndpointError::EndpointDataTooLarge { len, max });
251 };
252 converted.push(payload);
253 }
254 Ok(converted)
255}
256
257fn service_datagram_payloads(
258 datagrams: Vec<FipsEndpointOutboundDatagram>,
259) -> Result<Vec<EndpointDataPayload>, FipsEndpointError> {
260 let max = crate::node::session_wire::fsp_service_datagram_max_body_len();
261 let mut payloads = Vec::with_capacity(datagrams.len());
262 for datagram in datagrams {
263 let len = datagram.data.len();
264 let Some(payload) = EndpointDataPayload::from_service_datagram(
265 datagram.source_port,
266 datagram.destination_port,
267 datagram.data,
268 ) else {
269 return Err(FipsEndpointError::ServiceDatagramTooLarge { len, max });
270 };
271 payloads.push(payload);
272 }
273 Ok(payloads)
274}
275
276fn spawn_node_task(
277 mut node: Node,
278 shutdown_rx: oneshot::Receiver<()>,
279) -> JoinHandle<Result<(), NodeError>> {
280 tokio::spawn(async move {
281 tokio::pin!(shutdown_rx);
282 let loop_result = tokio::select! {
283 result = node.run_rx_loop() => result,
284 _ = &mut shutdown_rx => Ok(()),
285 };
286 let stop_result = if node.state().can_stop() {
287 node.stop().await
288 } else {
289 Ok(())
290 };
291 loop_result?;
292 stop_result
293 })
294}
295
296pub struct FipsEndpoint {
298 identity: PeerIdentity,
299 npub: String,
300 node_addr: NodeAddr,
301 address: FipsAddress,
302 discovery_scope: Option<String>,
303 outbound_packets: TunOutboundTx,
304 delivered_packets: Arc<Mutex<mpsc::Receiver<NodeDeliveredPacket>>>,
305 endpoint_control_tx: mpsc::Sender<NodeEndpointControlCommand>,
306 endpoint_data_batches: EndpointDataBatchTx,
307 inbound_endpoint_tx: EndpointEventSender,
313 inbound_endpoint_rx: Arc<Mutex<EndpointReceiveState>>,
320 inbound_service_tx: EndpointServiceEventSender,
321 inbound_service_rx: Arc<Mutex<ServiceReceiveState>>,
322 registered_services: Arc<StdMutex<HashMap<u16, EndpointServiceEventSender>>>,
323 service_channel_capacity: usize,
324 shutdown_tx: StdMutex<Option<oneshot::Sender<()>>>,
325 task: StdMutex<Option<JoinHandle<Result<(), NodeError>>>>,
326}
327
328impl FipsEndpoint {
329 pub fn builder() -> FipsEndpointBuilder {
331 FipsEndpointBuilder::default()
332 }
333
334 async fn control<T>(
335 &self,
336 operation: &'static str,
337 command: NodeEndpointControlCommand,
338 response_rx: oneshot::Receiver<T>,
339 ) -> Result<T, FipsEndpointError> {
340 tokio::time::timeout(ENDPOINT_OPERATION_TIMEOUT, async {
341 self.endpoint_control_tx
342 .send(command)
343 .await
344 .map_err(|_| FipsEndpointError::Closed)?;
345 response_rx.await.map_err(|_| FipsEndpointError::Closed)
346 })
347 .await
348 .map_err(|_| FipsEndpointError::Timeout { operation })?
349 }
350
351 pub fn npub(&self) -> &str {
353 &self.npub
354 }
355
356 pub fn node_addr(&self) -> &NodeAddr {
358 &self.node_addr
359 }
360
361 pub fn address(&self) -> FipsAddress {
363 self.address
364 }
365
366 pub fn discovery_scope(&self) -> Option<&str> {
368 self.discovery_scope.as_deref()
369 }
370
371 pub async fn send_batch_to_peer(
379 &self,
380 remote: PeerIdentity,
381 payloads: Vec<Vec<u8>>,
382 ) -> Result<(), FipsEndpointError> {
383 self.send_payloads_to_peer(remote, payloads)
384 }
385
386 pub async fn register_service(&self, port: u16) -> Result<(), FipsEndpointError> {
391 self.register_service_with_sender(port, self.inbound_service_tx.clone())
392 .await
393 }
394
395 pub async fn register_service_receiver(
397 &self,
398 port: u16,
399 ) -> Result<FipsEndpointServiceReceiver, FipsEndpointError> {
400 let (sender, receiver) = EndpointServiceEventSender::channel(self.service_channel_capacity);
401 self.register_service_with_sender(port, sender).await?;
402 Ok(FipsEndpointServiceReceiver {
403 state: Mutex::new(ServiceReceiveState::new(receiver)),
404 })
405 }
406
407 async fn register_service_with_sender(
408 &self,
409 port: u16,
410 sender: EndpointServiceEventSender,
411 ) -> Result<(), FipsEndpointError> {
412 if port == crate::node::session_wire::FSP_PORT_IPV6_SHIM {
413 return Err(FipsEndpointError::ServicePortReserved { port });
414 }
415
416 let (response_tx, response_rx) = oneshot::channel();
417 if !self
418 .control(
419 "service registration",
420 NodeEndpointControlCommand::RegisterService {
421 port,
422 sender: sender.clone(),
423 response_tx,
424 },
425 response_rx,
426 )
427 .await?
428 {
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 match self
766 .control(
767 "peer update",
768 NodeEndpointControlCommand::UpdatePeers { peers, response_tx },
769 response_rx,
770 )
771 .await?
772 {
773 Ok(outcome) => Ok(UpdatePeersOutcome::from(outcome)),
774 Err(error) => Err(FipsEndpointError::Node(error)),
775 }
776 }
777
778 pub async fn refresh_peer_paths(
786 &self,
787 peers: Vec<PeerIdentity>,
788 ) -> Result<usize, FipsEndpointError> {
789 let (response_tx, response_rx) = oneshot::channel();
790 let npubs = peers.into_iter().map(|peer| peer.npub()).collect();
791 match self
792 .control(
793 "peer path refresh",
794 NodeEndpointControlCommand::RefreshPeerPaths { npubs, response_tx },
795 response_rx,
796 )
797 .await?
798 {
799 Ok(refreshed) => Ok(refreshed),
800 Err(error) => Err(FipsEndpointError::Node(error)),
801 }
802 }
803
804 pub async fn peers(&self) -> Result<Vec<FipsEndpointPeer>, FipsEndpointError> {
806 let (response_tx, response_rx) = oneshot::channel();
807 self.control(
808 "peer snapshot",
809 NodeEndpointControlCommand::PeerSnapshot { response_tx },
810 response_rx,
811 )
812 .await
813 .map(|peers| peers.into_iter().map(FipsEndpointPeer::from).collect())
814 }
815
816 pub async fn peer_rating_events(
819 &self,
820 scope: impl Into<String>,
821 ) -> Result<Vec<nostr::Event>, FipsEndpointError> {
822 let (response_tx, response_rx) = oneshot::channel();
823 self.control(
824 "peer rating snapshot",
825 NodeEndpointControlCommand::PeerRatingEvents {
826 scope: scope.into(),
827 response_tx,
828 },
829 response_rx,
830 )
831 .await?
832 .map_err(FipsEndpointError::Node)
833 }
834
835 pub async fn ingest_nostr_discovery_event(
842 &self,
843 event: nostr::Event,
844 ) -> Result<bool, FipsEndpointError> {
845 let (response_tx, response_rx) = oneshot::channel();
846 self.control(
847 "Nostr event ingest",
848 NodeEndpointControlCommand::IngestNostrDiscoveryEvent { event, response_tx },
849 response_rx,
850 )
851 .await
852 }
853
854 #[deprecated(since = "0.3.98", note = "use ingest_nostr_discovery_event")]
855 pub async fn ingest_nostr_pubsub_event(
856 &self,
857 event: nostr::Event,
858 ) -> Result<bool, FipsEndpointError> {
859 self.ingest_nostr_discovery_event(event).await
860 }
861
862 pub async fn local_advertised_endpoints(
865 &self,
866 ) -> Result<Vec<crate::discovery::nostr::OverlayEndpointAdvert>, FipsEndpointError> {
867 let (response_tx, response_rx) = oneshot::channel();
868 self.control(
869 "local advert snapshot",
870 NodeEndpointControlCommand::LocalAdvertSnapshot { response_tx },
871 response_rx,
872 )
873 .await
874 }
875
876 pub async fn local_nostr_discovery_advert_event(
882 &self,
883 ) -> Result<Option<nostr::Event>, FipsEndpointError> {
884 let (response_tx, response_rx) = oneshot::channel();
885 match self
886 .control(
887 "local Nostr discovery advert",
888 NodeEndpointControlCommand::LocalNostrDiscoveryAdvertEvent { response_tx },
889 response_rx,
890 )
891 .await?
892 {
893 Ok(event) => Ok(event),
894 Err(error) => Err(FipsEndpointError::Node(error)),
895 }
896 }
897
898 pub async fn relay_statuses(&self) -> Result<Vec<FipsEndpointRelayStatus>, FipsEndpointError> {
900 let (response_tx, response_rx) = oneshot::channel();
901 self.control(
902 "relay snapshot",
903 NodeEndpointControlCommand::RelaySnapshot { response_tx },
904 response_rx,
905 )
906 .await
907 .map(|relays| {
908 relays
909 .into_iter()
910 .map(FipsEndpointRelayStatus::from)
911 .collect()
912 })
913 }
914
915 pub async fn update_relays(
917 &self,
918 advert_relays: Vec<String>,
919 dm_relays: Vec<String>,
920 ) -> Result<(), FipsEndpointError> {
921 let (response_tx, response_rx) = oneshot::channel();
922 self.control(
923 "relay update",
924 NodeEndpointControlCommand::UpdateRelays {
925 advert_relays,
926 dm_relays,
927 response_tx,
928 },
929 response_rx,
930 )
931 .await?
932 .map_err(FipsEndpointError::Node)
933 }
934
935 pub async fn send_ip_packet(
937 &self,
938 packet: impl Into<Vec<u8>>,
939 ) -> Result<(), FipsEndpointError> {
940 self.outbound_packets
941 .send(packet.into())
942 .await
943 .map_err(|_| FipsEndpointError::Closed)
944 }
945
946 pub async fn recv_ip_packet(&self) -> Option<NodeDeliveredPacket> {
948 self.delivered_packets.lock().await.recv().await
949 }
950
951 pub async fn shutdown(&self) -> Result<(), FipsEndpointError> {
953 let shutdown_tx = self
954 .shutdown_tx
955 .lock()
956 .map_err(|_| FipsEndpointError::Closed)?
957 .take();
958 if let Some(shutdown_tx) = shutdown_tx {
959 let _ = shutdown_tx.send(());
960 }
961 let task = self
962 .task
963 .lock()
964 .map_err(|_| FipsEndpointError::Closed)?
965 .take();
966 if let Some(mut task) = task {
967 match tokio::time::timeout(ENDPOINT_OPERATION_TIMEOUT, &mut task).await {
968 Ok(result) => result??,
969 Err(_) => {
970 task.abort();
971 let _ = task.await;
972 return Err(FipsEndpointError::Timeout {
973 operation: "shutdown",
974 });
975 }
976 }
977 }
978 Ok(())
979 }
980}
981
982impl Drop for FipsEndpoint {
983 fn drop(&mut self) {
984 if let Ok(mut shutdown_tx) = self.shutdown_tx.lock()
985 && let Some(shutdown_tx) = shutdown_tx.take()
986 {
987 let _ = shutdown_tx.send(());
988 }
989 if let Ok(mut task) = self.task.lock()
990 && let Some(task) = task.take()
991 {
992 task.abort();
993 }
994 }
995}