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