fips_core/node/lifecycle/
control.rs1use super::*;
2
3impl Node {
4 pub(crate) async fn api_connect(
12 &mut self,
13 npub: &str,
14 address: &str,
15 transport: &str,
16 ) -> Result<serde_json::Value, String> {
17 let peer_config = PeerConfig {
18 npub: npub.to_string(),
19 alias: None,
20 addresses: vec![PeerAddress::new(transport, address)],
21 connect_policy: ConnectPolicy::Manual,
22 auto_reconnect: false,
23 discovery_fallback_transit: true,
24 };
25
26 if let Ok(identity) = PeerIdentity::from_npub(npub) {
28 self.peer_aliases
29 .insert(*identity.node_addr(), identity.short_npub());
30 self.register_identity(*identity.node_addr(), identity.pubkey_full());
31 }
32
33 self.initiate_peer_connection(&peer_config)
34 .await
35 .map(|()| {
36 info!(
37 npub = %npub,
38 address = %address,
39 transport = %transport,
40 "API connect initiated"
41 );
42 serde_json::json!({
43 "npub": npub,
44 "address": address,
45 "transport": transport,
46 })
47 })
48 .map_err(|e| e.to_string())
49 }
50
51 pub(crate) fn api_disconnect(&mut self, npub: &str) -> Result<serde_json::Value, String> {
55 let peer_identity =
56 PeerIdentity::from_npub(npub).map_err(|e| format!("invalid npub '{npub}': {e}"))?;
57 let node_addr = *peer_identity.node_addr();
58
59 if !self.peers.contains_key(&node_addr) {
60 return Err(format!("peer not found: {npub}"));
61 }
62
63 self.remove_active_peer(&node_addr);
65
66 self.retry_pending.remove(&node_addr);
68
69 info!(npub = %npub, "API disconnect completed");
70
71 Ok(serde_json::json!({
72 "npub": npub,
73 "disconnected": true,
74 }))
75 }
76
77 pub async fn adopt_established_traversal(
84 &mut self,
85 traversal: EstablishedTraversal,
86 ) -> Result<BootstrapHandoffResult, NodeError> {
87 debug!(
88 peer_npub = %traversal.peer_npub,
89 session_id = %traversal.session_id,
90 remote_addr = %traversal.remote_addr,
91 "adopting established traversal socket"
92 );
93
94 if !self.state.is_operational() {
95 return Err(NodeError::NotStarted);
96 }
97
98 let packet_tx = self.packet_tx.clone().ok_or(NodeError::NotStarted)?;
99 let peer_identity = PeerIdentity::from_npub(&traversal.peer_npub).map_err(|e| {
100 NodeError::InvalidPeerNpub {
101 npub: traversal.peer_npub.clone(),
102 reason: e.to_string(),
103 }
104 })?;
105 let peer_node_addr = *peer_identity.node_addr();
106 if self.peers.contains_key(&peer_node_addr) {
107 debug!(
108 peer_npub = %traversal.peer_npub,
109 "Adopting NAT traversal handoff as alternate path for already-connected peer"
110 );
111 }
112
113 self.peer_aliases
114 .insert(peer_node_addr, peer_identity.short_npub());
115 self.register_identity(peer_node_addr, peer_identity.pubkey_full());
116
117 let transport_id = self.allocate_transport_id();
118 let inherited_config = traversal.transport_config.clone().unwrap_or_else(|| {
138 let mut cfg = self
139 .lookup_udp_config(traversal.transport_name.as_deref())
140 .or_else(|| self.lookup_udp_config(None))
141 .cloned()
142 .unwrap_or_default();
143 cfg.bind_addr = None;
144 cfg.external_addr = None;
145 cfg
146 });
147 let mut transport = crate::transport::udp::UdpTransport::new(
148 transport_id,
149 traversal.transport_name.clone(),
150 inherited_config,
151 packet_tx,
152 );
153
154 transport
155 .adopt_socket_async(traversal.socket)
156 .await
157 .map_err(|e| NodeError::BootstrapHandoff(e.to_string()))?;
158
159 let local_addr = transport.local_addr().ok_or_else(|| {
160 NodeError::BootstrapHandoff("adopted UDP transport has no local address".into())
161 })?;
162
163 self.transports.insert(
164 transport_id,
165 crate::transport::TransportHandle::Udp(transport),
166 );
167 self.bootstrap_transports
168 .register(transport_id, traversal.peer_npub.clone());
169
170 let remote_addr = TransportAddr::from_string(&traversal.remote_addr.to_string());
171 if let Err(err) = self
172 .initiate_connection(transport_id, remote_addr.clone(), peer_identity)
173 .await
174 {
175 self.bootstrap_transports.remove(&transport_id);
176 if let Some(mut handle) = self.transports.remove(&transport_id) {
177 let _ = handle.stop().await;
178 }
179 return Err(err);
180 }
181
182 info!(
183 peer = %self.peer_display_name(&peer_node_addr),
184 transport_id = %transport_id,
185 local_addr = %local_addr,
186 remote_addr = %traversal.remote_addr,
187 session_id = %traversal.session_id,
188 "adopted NAT traversal socket; handshake initiated"
189 );
190
191 Ok(BootstrapHandoffResult {
192 transport_id,
193 local_addr,
194 remote_addr: traversal.remote_addr,
195 peer_node_addr,
196 session_id: traversal.session_id,
197 })
198 }
199}