1use crate::client::base_client::storage::helpers::{
7 has_gateway_details, load_active_gateway_details, load_client_keys, load_gateway_details,
8 store_gateway_details, update_stored_published_data_gateway,
9};
10use crate::client::key_manager::persistence::KeyStore;
11use crate::client::key_manager::ClientKeys;
12use crate::error::ClientCoreError;
13use crate::init::helpers::{
14 choose_gateway_by_latency, get_specified_gateway, uniformly_random_gateway,
15};
16use crate::init::types::{
17 GatewaySelectionSpecification, GatewaySetup, InitialisationResult, SelectedGateway,
18};
19use nym_client_core_gateways_storage::{GatewayDetails, GatewayRegistration};
20use nym_client_core_gateways_storage::{GatewayPublishedData, GatewaysDetailsStore};
21use nym_gateway_client::client::InitGatewayClient;
22use nym_topology::node::RoutingNode;
23use rand::rngs::OsRng;
24use rand::{CryptoRng, RngCore};
25use serde::Serialize;
26#[cfg(unix)]
27use std::{os::fd::RawFd, sync::Arc};
28
29pub mod helpers;
30pub mod types;
31#[cfg(not(target_arch = "wasm32"))]
32pub(crate) mod websockets;
33
34pub async fn generate_new_client_keys<K, R>(
37 rng: &mut R,
38 key_store: &K,
39) -> Result<(), ClientCoreError>
40where
41 R: RngCore + CryptoRng,
42 K: KeyStore,
43 K::StorageError: Send + Sync + 'static,
44{
45 ClientKeys::generate_new(rng)
46 .persist_keys(key_store)
47 .await
48 .map_err(|source| ClientCoreError::KeyStoreError {
49 source: Box::new(source),
50 })
51}
52
53async fn setup_new_gateway<K, D>(
54 key_store: &K,
55 details_store: &D,
56 selection_specification: GatewaySelectionSpecification,
57 available_gateways: Vec<RoutingNode>,
58 #[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
59) -> Result<InitialisationResult, ClientCoreError>
60where
61 K: KeyStore,
62 D: GatewaysDetailsStore,
63 K::StorageError: Send + Sync + 'static,
64 D::StorageError: Send + Sync + 'static,
65{
66 tracing::trace!("Setting up new gateway");
67
68 let client_keys = load_client_keys(key_store).await?;
70
71 let mut rng = OsRng;
72
73 let selected_gateway = match selection_specification {
74 GatewaySelectionSpecification::UniformRemote {
75 must_use_tls,
76 no_hostname,
77 } => {
78 let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?;
79 SelectedGateway::from_topology_node(gateway, must_use_tls, no_hostname)?
80 }
81 GatewaySelectionSpecification::RemoteByLatency {
82 must_use_tls,
83 no_hostname,
84 } => {
85 let gateway =
86 choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?;
87 SelectedGateway::from_topology_node(gateway, must_use_tls, no_hostname)?
88 }
89 GatewaySelectionSpecification::Specified {
90 must_use_tls,
91 no_hostname,
92 identity,
93 } => {
94 let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?;
95 SelectedGateway::from_topology_node(gateway, must_use_tls, no_hostname)?
96 }
97 GatewaySelectionSpecification::Custom {
98 gateway_identity,
99 additional_data,
100 } => SelectedGateway::custom(gateway_identity, additional_data)?,
101 };
102
103 let selected_id = selected_gateway.gateway_id().to_base58_string();
106 if has_gateway_details(details_store, &selected_id).await? {
107 return Err(ClientCoreError::AlreadyRegistered {
108 gateway_id: selected_id,
109 });
110 }
111
112 let (gateway_details, authenticated_ephemeral_client) = match selected_gateway {
113 SelectedGateway::Remote {
114 gateway_id,
115
116 gateway_listeners,
117 } => {
118 let our_identity = client_keys.identity_keypair();
120
121 let registration = helpers::register_with_gateway(
122 gateway_id,
123 gateway_listeners.clone(),
124 our_identity,
125 #[cfg(unix)]
126 connection_fd_callback,
127 )
128 .await?;
129 (
130 GatewayDetails::new_remote(
131 gateway_id,
132 registration.shared_keys,
133 GatewayPublishedData::new(gateway_listeners),
134 ),
135 Some(registration.authenticated_ephemeral_client),
136 )
137 }
138 SelectedGateway::Custom {
139 gateway_id,
140 additional_data,
141 } => (
142 GatewayDetails::new_custom(gateway_id, additional_data),
143 None,
144 ),
145 };
146
147 let gateway_registration = gateway_details.into();
148
149 store_gateway_details(details_store, &gateway_registration).await?;
151
152 Ok(InitialisationResult {
153 gateway_registration,
154 client_keys,
155 authenticated_ephemeral_client,
156 })
157}
158
159pub async fn refresh_gateway_published_data<D>(
160 details_store: &D,
161 registration: GatewayRegistration,
162 available_gateways: Vec<RoutingNode>,
163 must_use_tls: bool,
164 no_hostname: bool,
165) -> Result<(), ClientCoreError>
166where
167 D: GatewaysDetailsStore,
168 D::StorageError: Send + Sync + 'static,
169{
170 let gateway_id = registration.gateway_id().to_base58_string();
171 tracing::trace!("Updating gateway details : {gateway_id}");
172
173 let gateway = get_specified_gateway(&gateway_id, &available_gateways, must_use_tls)?;
174 let selected_gateway = SelectedGateway::from_topology_node(gateway, must_use_tls, no_hostname)?;
175
176 let new_gateway_listeners = match selected_gateway {
177 SelectedGateway::Remote {
178 gateway_listeners, ..
179 } => gateway_listeners,
180 SelectedGateway::Custom { .. } => {
181 Err(ClientCoreError::UnexpectedCustomGatewaySelection)?
183 }
184 };
185
186 let new_published_data = GatewayPublishedData::new(new_gateway_listeners);
187
188 update_stored_published_data_gateway(
190 details_store,
191 ®istration.gateway_id(),
192 &new_published_data,
193 )
194 .await?;
195
196 Ok(())
197}
198
199async fn use_loaded_gateway_details<K, D>(
200 key_store: &K,
201 details_store: &D,
202 gateway_id: Option<String>,
203) -> Result<InitialisationResult, ClientCoreError>
204where
205 K: KeyStore,
206 D: GatewaysDetailsStore,
207 K::StorageError: Send + Sync + 'static,
208 D::StorageError: Send + Sync + 'static,
209{
210 let loaded_details = if let Some(gateway_id) = gateway_id {
211 load_gateway_details(details_store, &gateway_id).await?
212 } else {
213 load_active_gateway_details(details_store)
214 .await?
215 .registration
216 .ok_or(ClientCoreError::NoActiveGatewaySet)?
217 };
218
219 let loaded_keys = load_client_keys(key_store).await?;
220
221 Ok(InitialisationResult::new_loaded(
223 loaded_details,
224 loaded_keys,
225 ))
226}
227
228fn reuse_gateway_connection(
229 authenticated_ephemeral_client: InitGatewayClient,
230 gateway_registration: GatewayRegistration,
231 client_keys: ClientKeys,
232) -> InitialisationResult {
233 InitialisationResult {
234 gateway_registration,
235 client_keys,
236 authenticated_ephemeral_client: Some(authenticated_ephemeral_client),
237 }
238}
239
240pub async fn setup_gateway<K, D>(
241 setup: GatewaySetup,
242 key_store: &K,
243 details_store: &D,
244) -> Result<InitialisationResult, ClientCoreError>
245where
246 K: KeyStore,
247 D: GatewaysDetailsStore,
248 K::StorageError: Send + Sync + 'static,
249 D::StorageError: Send + Sync + 'static,
250{
251 tracing::debug!("Setting up gateway");
252 match setup {
253 GatewaySetup::MustLoad { gateway_id } => {
254 tracing::debug!("GatewaySetup::MustLoad with id: {gateway_id:?}");
255 use_loaded_gateway_details(key_store, details_store, gateway_id).await
256 }
257 GatewaySetup::New {
258 specification,
259 available_gateways,
260 #[cfg(unix)]
261 connection_fd_callback,
262 } => {
263 tracing::debug!("GatewaySetup::New with spec: {specification:?}");
264 setup_new_gateway(
265 key_store,
266 details_store,
267 specification,
268 available_gateways,
269 #[cfg(unix)]
270 connection_fd_callback,
271 )
272 .await
273 }
274 GatewaySetup::ReuseConnection {
275 authenticated_ephemeral_client,
276 gateway_details,
277 client_keys: managed_keys,
278 } => {
279 tracing::debug!("GatewaySetup::ReuseConnection");
280 Ok(reuse_gateway_connection(
281 *authenticated_ephemeral_client,
282 *gateway_details,
283 managed_keys,
284 ))
285 }
286 }
287}
288
289pub fn output_to_json<T: Serialize>(init_results: &T, output_file: &str) {
290 match std::fs::File::create(output_file) {
291 Ok(file) => match serde_json::to_writer_pretty(file, init_results) {
292 Ok(_) => println!("Saved: {output_file}"),
293 Err(err) => eprintln!("Could not save {output_file}: {err}"),
294 },
295 Err(err) => eprintln!("Could not save {output_file}: {err}"),
296 }
297}