1use crate::models::{BinaryBuildInformationOwned, OffsetDateTimeJsonSchemaWrapper};
5use crate::nym_nodes::{BasicEntryInformation, NodeRole, SemiSkimmedNode, SkimmedNode};
6use nym_crypto::asymmetric::ed25519::serde_helpers::bs58_ed25519_pubkey;
7use nym_crypto::asymmetric::x25519::serde_helpers::bs58_x25519_pubkey;
8use nym_crypto::asymmetric::{ed25519, x25519};
9use nym_mixnet_contract_common::reward_params::Performance;
10use nym_mixnet_contract_common::NodeId;
11use nym_network_defaults::{
12 DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, WG_METADATA_PORT, WG_TUNNEL_PORT,
13};
14use nym_node_requests::api::v1::authenticator::models::Authenticator;
15use nym_node_requests::api::v1::gateway::models::Wireguard;
16use nym_node_requests::api::v1::ip_packet_router::models::IpPacketRouter;
17use nym_node_requests::api::v1::lewes_protocol::models::LewesProtocol;
18use nym_node_requests::api::v1::node::models::{AuxiliaryDetails, NodeRoles};
19use nym_noise_keys::VersionedNoiseKey;
20use serde::{Deserialize, Serialize};
21use std::net::IpAddr;
22use tracing::warn;
23use utoipa::ToSchema;
24
25#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
26pub struct HostInformation {
27 #[schema(value_type = Vec<String>)]
28 pub ip_address: Vec<IpAddr>,
29 pub hostname: Option<String>,
30 pub keys: HostKeys,
31}
32
33impl From<nym_node_requests::api::v1::node::models::HostInformation> for HostInformation {
34 fn from(value: nym_node_requests::api::v1::node::models::HostInformation) -> Self {
35 HostInformation {
36 ip_address: value.ip_address,
37 hostname: value.hostname,
38 keys: value.keys.into(),
39 }
40 }
41}
42
43#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
44pub struct HostKeys {
45 #[serde(with = "bs58_ed25519_pubkey")]
46 #[schemars(with = "String")]
47 #[schema(value_type = String)]
48 pub ed25519: ed25519::PublicKey,
49
50 #[deprecated(note = "use the current_x25519_sphinx_key with explicit rotation information")]
51 #[serde(with = "bs58_x25519_pubkey")]
52 #[schemars(with = "String")]
53 #[schema(value_type = String)]
54 pub x25519: x25519::PublicKey,
55
56 pub current_x25519_sphinx_key: SphinxKey,
57
58 #[serde(default)]
59 pub pre_announced_x25519_sphinx_key: Option<SphinxKey>,
60
61 #[serde(default)]
62 pub x25519_versioned_noise: Option<VersionedNoiseKey>,
63}
64
65#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
66pub struct SphinxKey {
67 pub rotation_id: u32,
68
69 #[serde(with = "bs58_x25519_pubkey")]
70 #[schemars(with = "String")]
71 #[schema(value_type = String)]
72 pub public_key: x25519::PublicKey,
73}
74
75impl From<nym_node_requests::api::v1::node::models::SphinxKey> for SphinxKey {
76 fn from(value: nym_node_requests::api::v1::node::models::SphinxKey) -> Self {
77 SphinxKey {
78 rotation_id: value.rotation_id,
79 public_key: value.public_key,
80 }
81 }
82}
83
84impl From<nym_node_requests::api::v1::node::models::HostKeys> for HostKeys {
85 fn from(value: nym_node_requests::api::v1::node::models::HostKeys) -> Self {
86 HostKeys {
87 ed25519: value.ed25519_identity,
88 x25519: value.x25519_sphinx,
89 current_x25519_sphinx_key: value.primary_x25519_sphinx_key.into(),
90 pre_announced_x25519_sphinx_key: value.pre_announced_x25519_sphinx_key.map(Into::into),
91 x25519_versioned_noise: value.x25519_versioned_noise,
92 }
93 }
94}
95
96#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
97pub struct WebSockets {
98 pub ws_port: u16,
99
100 pub wss_port: Option<u16>,
101}
102
103impl From<nym_node_requests::api::v1::gateway::models::WebSockets> for WebSockets {
104 fn from(value: nym_node_requests::api::v1::gateway::models::WebSockets) -> Self {
105 WebSockets {
106 ws_port: value.ws_port,
107 wss_port: value.wss_port,
108 }
109 }
110}
111
112#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
113pub struct NoiseDetails {
114 pub key: VersionedNoiseKey,
115
116 pub mixnet_port: u16,
117
118 #[schema(value_type = Vec<String>)]
119 pub ip_addresses: Vec<IpAddr>,
120}
121
122#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
123pub struct NymNodeDescription {
124 #[schema(value_type = u32)]
125 pub node_id: NodeId,
126 pub contract_node_type: DescribedNodeType,
127 pub description: NymNodeData,
128}
129
130impl NymNodeDescription {
131 pub fn version(&self) -> &str {
132 &self.description.build_information.build_version
133 }
134
135 pub fn entry_information(&self) -> BasicEntryInformation {
136 BasicEntryInformation {
137 hostname: self.description.host_information.hostname.clone(),
138 ws_port: self.description.mixnet_websockets.ws_port,
139 wss_port: self.description.mixnet_websockets.wss_port,
140 }
141 }
142
143 pub fn ed25519_identity_key(&self) -> ed25519::PublicKey {
144 self.description.host_information.keys.ed25519
145 }
146
147 pub fn current_sphinx_key(&self, current_rotation_id: u32) -> x25519::PublicKey {
148 let keys = &self.description.host_information.keys;
149
150 if keys.current_x25519_sphinx_key.rotation_id == u32::MAX {
151 return keys.current_x25519_sphinx_key.public_key;
153 }
154
155 if current_rotation_id == keys.current_x25519_sphinx_key.rotation_id {
156 return keys.current_x25519_sphinx_key.public_key;
158 }
159
160 if let Some(pre_announced) = &keys.pre_announced_x25519_sphinx_key {
161 if pre_announced.rotation_id == current_rotation_id {
162 return pre_announced.public_key;
163 }
164 }
165
166 warn!(
167 "unexpected key rotation {current_rotation_id} for node {}",
168 self.node_id
169 );
170 keys.current_x25519_sphinx_key.public_key
172 }
173
174 pub fn to_skimmed_node(
175 &self,
176 current_rotation_id: u32,
177 role: NodeRole,
178 performance: Performance,
179 ) -> SkimmedNode {
180 let keys = &self.description.host_information.keys;
181 let entry = if self.description.declared_role.entry {
182 Some(self.entry_information())
183 } else {
184 None
185 };
186
187 SkimmedNode {
188 node_id: self.node_id,
189 ed25519_identity_pubkey: keys.ed25519,
190 ip_addresses: self.description.host_information.ip_address.clone(),
191 mix_port: self.description.mix_port(),
192 x25519_sphinx_pubkey: self.current_sphinx_key(current_rotation_id),
193 role,
197 supported_roles: self.description.declared_role,
198 entry,
199 performance,
200 }
201 }
202
203 pub fn to_semi_skimmed_node(
204 &self,
205 current_rotation_id: u32,
206 role: NodeRole,
207 performance: Performance,
208 ) -> SemiSkimmedNode {
209 let skimmed_node = self.to_skimmed_node(current_rotation_id, role, performance);
210
211 SemiSkimmedNode {
212 basic: skimmed_node,
213 x25519_noise_versioned_key: self
214 .description
215 .host_information
216 .keys
217 .x25519_versioned_noise,
218 }
219 }
220}
221
222#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
223#[serde(rename_all = "snake_case")]
224#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
225#[cfg_attr(
226 feature = "generate-ts",
227 ts(
228 export,
229 export_to = "ts-packages/types/src/types/rust/DescribedNodeType.ts"
230 )
231)]
232pub enum DescribedNodeType {
233 LegacyMixnode,
234 LegacyGateway,
235 NymNode,
236}
237
238impl DescribedNodeType {
239 pub fn is_nym_node(&self) -> bool {
240 matches!(self, DescribedNodeType::NymNode)
241 }
242}
243
244#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
245#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
246#[cfg_attr(
247 feature = "generate-ts",
248 ts(
249 export,
250 export_to = "ts-packages/types/src/types/rust/DeclaredRoles.ts"
251 )
252)]
253pub struct DeclaredRoles {
254 pub mixnode: bool,
255 pub entry: bool,
256 pub exit_nr: bool,
257 pub exit_ipr: bool,
258}
259
260impl DeclaredRoles {
261 pub fn can_operate_exit_gateway(&self) -> bool {
262 self.exit_ipr && self.exit_nr
263 }
264}
265
266impl From<NodeRoles> for DeclaredRoles {
267 fn from(value: NodeRoles) -> Self {
268 DeclaredRoles {
269 mixnode: value.mixnode_enabled,
270 entry: value.gateway_enabled,
271 exit_nr: value.gateway_enabled && value.network_requester_enabled,
272 exit_ipr: value.gateway_enabled && value.ip_packet_router_enabled,
273 }
274 }
275}
276
277#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
278pub struct NetworkRequesterDetails {
279 pub address: String,
281
282 pub uses_exit_policy: bool,
284}
285
286#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
287pub struct IpPacketRouterDetails {
288 pub address: String,
290}
291
292impl From<IpPacketRouter> for IpPacketRouterDetails {
294 fn from(value: IpPacketRouter) -> Self {
295 IpPacketRouterDetails {
296 address: value.address,
297 }
298 }
299}
300
301#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
302pub struct AuthenticatorDetails {
303 pub address: String,
305}
306
307impl From<Authenticator> for AuthenticatorDetails {
309 fn from(value: Authenticator) -> Self {
310 AuthenticatorDetails {
311 address: value.address,
312 }
313 }
314}
315#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
316pub struct WireguardDetails {
317 pub port: u16,
319 #[serde(default = "default_tunnel_port")]
320 pub tunnel_port: u16,
321 #[serde(default = "default_metadata_port")]
322 pub metadata_port: u16,
323 pub public_key: String,
324}
325
326fn default_tunnel_port() -> u16 {
327 WG_TUNNEL_PORT
328}
329fn default_metadata_port() -> u16 {
330 WG_METADATA_PORT
331}
332
333impl From<Wireguard> for WireguardDetails {
335 fn from(value: Wireguard) -> Self {
336 WireguardDetails {
337 port: value.port,
338 tunnel_port: value.tunnel_port,
339 metadata_port: value.metadata_port,
340 public_key: value.public_key,
341 }
342 }
343}
344
345#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
346pub struct LewesProtocolDetails {
347 pub enabled: bool,
350
351 pub control_port: u16,
353
354 pub data_port: u16,
356}
357
358impl From<LewesProtocol> for LewesProtocolDetails {
359 fn from(value: LewesProtocol) -> Self {
360 LewesProtocolDetails {
361 enabled: value.enabled,
362 control_port: value.control_port,
363 data_port: value.data_port,
364 }
365 }
366}
367
368#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
370pub struct NymNodeData {
371 #[serde(default)]
372 pub last_polled: OffsetDateTimeJsonSchemaWrapper,
373
374 pub host_information: HostInformation,
375
376 #[serde(default)]
377 pub declared_role: DeclaredRoles,
378
379 #[serde(default)]
380 pub auxiliary_details: AuxiliaryDetails,
381
382 pub build_information: BinaryBuildInformationOwned,
384
385 #[serde(default)]
386 pub network_requester: Option<NetworkRequesterDetails>,
387
388 #[serde(default)]
389 pub ip_packet_router: Option<IpPacketRouterDetails>,
390
391 #[serde(default)]
392 pub authenticator: Option<AuthenticatorDetails>,
393
394 #[serde(default)]
395 pub wireguard: Option<WireguardDetails>,
396
397 #[serde(default)]
398 pub lewes_protocol: Option<LewesProtocolDetails>,
399
400 pub mixnet_websockets: WebSockets,
402}
403
404impl NymNodeData {
405 pub fn mix_port(&self) -> u16 {
406 self.auxiliary_details
407 .announce_ports
408 .mix_port
409 .unwrap_or(DEFAULT_MIX_LISTENING_PORT)
410 }
411
412 pub fn verloc_port(&self) -> u16 {
413 self.auxiliary_details
414 .announce_ports
415 .verloc_port
416 .unwrap_or(DEFAULT_VERLOC_LISTENING_PORT)
417 }
418}