1use celes::Country;
5use nym_crypto::asymmetric::ed25519::{self, serde_helpers::bs58_ed25519_pubkey};
6use nym_crypto::asymmetric::x25519::{
7 self, serde_helpers::bs58_x25519_pubkey, serde_helpers::option_bs58_x25519_pubkey,
8};
9use nym_noise_keys::VersionedNoiseKeyV1;
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12use std::net::IpAddr;
13
14pub use crate::api::SignedHostInformation;
15use crate::api::v2::node::models::AuxiliaryDetailsV2;
16pub use nym_bin_common::build_information::BinaryBuildInformationOwned;
17
18#[derive(Clone, Default, Debug, Copy, Serialize, Deserialize, JsonSchema)]
19#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
20pub struct NodeRoles {
21 pub mixnode_enabled: bool,
22 pub gateway_enabled: bool,
23 pub network_requester_enabled: bool,
24 pub ip_packet_router_enabled: bool,
25}
26
27impl NodeRoles {
28 pub fn can_operate_mixnode(&self) -> bool {
29 self.mixnode_enabled
30 }
31
32 pub fn can_operate_entry_gateway(&self) -> bool {
33 self.gateway_enabled
34 }
35
36 pub fn can_operate_exit_gateway(&self) -> bool {
37 self.gateway_enabled && self.network_requester_enabled && self.ip_packet_router_enabled
38 }
39}
40
41#[derive(Clone, Copy, Default, Debug, Serialize, Deserialize, JsonSchema)]
42#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
43pub struct AnnouncePorts {
44 pub verloc_port: Option<u16>,
45 pub mix_port: Option<u16>,
46}
47
48#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
49#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
50pub struct HostInformation {
51 #[cfg_attr(feature = "openapi", schema(value_type = Vec<String>, format = Byte, example = json!(["1.1.1.1"])))]
53 pub ip_address: Vec<IpAddr>,
54
55 #[cfg_attr(feature = "openapi", schema(example = "nymtech.net"))]
57 pub hostname: Option<String>,
58
59 pub keys: HostKeys,
61}
62
63impl HostInformation {
64 pub fn check_ips(&self) -> bool {
65 for ip in &self.ip_address {
66 if ip.is_unspecified() || ip.is_loopback() || ip.is_multicast() {
67 return false;
68 }
69 }
70 true
71 }
72}
73
74#[derive(Serialize)]
75pub struct LegacyHostInformationV3 {
76 pub ip_address: Vec<IpAddr>,
77 pub hostname: Option<String>,
78 pub keys: LegacyHostKeysV3,
79}
80
81#[derive(Serialize)]
82pub struct LegacyHostInformationV2 {
83 pub ip_address: Vec<IpAddr>,
84 pub hostname: Option<String>,
85 pub keys: LegacyHostKeysV2,
86}
87
88#[derive(Serialize)]
89pub struct LegacyHostInformationV1 {
90 pub ip_address: Vec<IpAddr>,
91 pub hostname: Option<String>,
92 pub keys: LegacyHostKeysV1,
93}
94
95impl From<HostInformation> for LegacyHostInformationV3 {
96 fn from(value: HostInformation) -> Self {
97 LegacyHostInformationV3 {
98 ip_address: value.ip_address,
99 hostname: value.hostname,
100 keys: value.keys.into(),
101 }
102 }
103}
104
105impl From<LegacyHostInformationV3> for LegacyHostInformationV2 {
106 fn from(value: LegacyHostInformationV3) -> Self {
107 LegacyHostInformationV2 {
108 ip_address: value.ip_address,
109 hostname: value.hostname,
110 keys: value.keys.into(),
111 }
112 }
113}
114
115impl From<LegacyHostInformationV2> for LegacyHostInformationV1 {
116 fn from(value: LegacyHostInformationV2) -> Self {
117 LegacyHostInformationV1 {
118 ip_address: value.ip_address,
119 hostname: value.hostname,
120 keys: value.keys.into(),
121 }
122 }
123}
124
125#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
126#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
127#[serde(from = "HostKeysDeHelper")]
128pub struct HostKeys {
129 #[schemars(with = "String")]
131 #[cfg_attr(feature = "openapi", schema(value_type = String))]
132 #[serde(with = "bs58_ed25519_pubkey")]
133 pub ed25519_identity: ed25519::PublicKey,
134
135 #[deprecated(note = "use explicit primary_x25519_sphinx_key instead")]
136 #[schemars(with = "String")]
137 #[cfg_attr(feature = "openapi", schema(value_type = String))]
138 #[serde(with = "bs58_x25519_pubkey")]
139 pub x25519_sphinx: x25519::PublicKey,
140
141 pub primary_x25519_sphinx_key: SphinxKey,
144
145 pub pre_announced_x25519_sphinx_key: Option<SphinxKey>,
147
148 #[serde(default)]
150 pub x25519_versioned_noise: Option<VersionedNoiseKeyV1>,
151}
152
153#[allow(deprecated)]
155impl From<HostKeysDeHelper> for HostKeys {
156 fn from(value: HostKeysDeHelper) -> Self {
157 let primary_x25519_sphinx_key = match value.primary_x25519_sphinx_key {
158 None => {
159 SphinxKey::new_legacy(value.x25519_sphinx)
161 }
162 Some(primary_x25519_sphinx_key) => primary_x25519_sphinx_key,
163 };
164
165 HostKeys {
166 ed25519_identity: value.ed25519_identity,
167 x25519_sphinx: value.x25519_sphinx,
168 primary_x25519_sphinx_key,
169 pre_announced_x25519_sphinx_key: value.pre_announced_x25519_sphinx_key,
170 x25519_versioned_noise: value.x25519_versioned_noise,
171 }
172 }
173}
174
175#[derive(Debug, Serialize, Deserialize)]
176struct HostKeysDeHelper {
177 #[serde(alias = "ed25519")]
179 #[serde(with = "bs58_ed25519_pubkey")]
180 pub ed25519_identity: ed25519::PublicKey,
181
182 #[deprecated(note = "use explicit primary_x25519_sphinx_key instead")]
183 #[serde(alias = "x25519")]
184 #[serde(with = "bs58_x25519_pubkey")]
185 pub x25519_sphinx: x25519::PublicKey,
186
187 pub primary_x25519_sphinx_key: Option<SphinxKey>,
190
191 #[serde(default)]
193 pub pre_announced_x25519_sphinx_key: Option<SphinxKey>,
194
195 #[serde(default)]
197 pub x25519_versioned_noise: Option<VersionedNoiseKeyV1>,
198}
199
200#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
201#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
202pub struct SphinxKey {
203 pub rotation_id: u32,
204
205 #[serde(with = "bs58_x25519_pubkey")]
206 #[schemars(with = "String")]
207 #[cfg_attr(feature = "openapi", schema(value_type = String))]
208 pub public_key: x25519::PublicKey,
209}
210
211impl SphinxKey {
212 pub fn new_legacy(public_key: x25519::PublicKey) -> SphinxKey {
213 SphinxKey {
214 rotation_id: u32::MAX,
215 public_key,
216 }
217 }
218
219 pub fn is_legacy(&self) -> bool {
220 self.rotation_id == u32::MAX
221 }
222}
223
224#[derive(Serialize)]
225pub struct LegacyHostKeysV3 {
226 #[serde(alias = "ed25519")]
227 #[serde(with = "bs58_ed25519_pubkey")]
228 pub ed25519_identity: ed25519::PublicKey,
229
230 #[serde(alias = "x25519")]
231 #[serde(with = "bs58_x25519_pubkey")]
232 pub x25519_sphinx: x25519::PublicKey,
233
234 #[serde(default)]
235 #[serde(with = "option_bs58_x25519_pubkey")]
236 pub x25519_noise: Option<x25519::PublicKey>,
237}
238
239#[derive(Serialize)]
240pub struct LegacyHostKeysV2 {
241 pub ed25519_identity: String,
242 pub x25519_sphinx: String,
243 pub x25519_noise: String,
244}
245
246#[derive(Serialize)]
247pub struct LegacyHostKeysV1 {
248 pub ed25519: String,
249 pub x25519: String,
250}
251
252impl From<HostKeys> for LegacyHostKeysV3 {
253 fn from(value: HostKeys) -> Self {
254 LegacyHostKeysV3 {
255 ed25519_identity: value.ed25519_identity,
256 x25519_sphinx: value.primary_x25519_sphinx_key.public_key,
257 x25519_noise: value.x25519_versioned_noise.map(|k| k.x25519_pubkey),
258 }
259 }
260}
261
262impl From<LegacyHostKeysV3> for LegacyHostKeysV2 {
263 fn from(value: LegacyHostKeysV3) -> Self {
264 LegacyHostKeysV2 {
265 ed25519_identity: value.ed25519_identity.to_base58_string(),
266 x25519_sphinx: value.x25519_sphinx.to_base58_string(),
267 x25519_noise: value
268 .x25519_noise
269 .map(|k| k.to_base58_string())
270 .unwrap_or_default(),
271 }
272 }
273}
274
275impl From<LegacyHostKeysV2> for LegacyHostKeysV1 {
276 fn from(value: LegacyHostKeysV2) -> Self {
277 LegacyHostKeysV1 {
278 ed25519: value.ed25519_identity,
279 x25519: value.x25519_sphinx,
280 }
281 }
282}
283
284#[derive(Clone, Default, Debug, Serialize, Deserialize, JsonSchema)]
285#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
286pub struct HostSystem {
287 pub system_name: Option<String>,
289
290 pub kernel_version: Option<String>,
292
293 pub os_version: Option<String>,
295
296 pub cpu_arch: Option<String>,
298
299 pub hardware: Option<Hardware>,
301}
302
303#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
304#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
305pub struct Hardware {
306 pub cpu: Vec<Cpu>,
308
309 pub total_memory: u64,
311
312 pub crypto: Option<CryptoHardware>,
314}
315
316#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
317#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
318pub struct Cpu {
319 pub name: String,
320
321 pub frequency: u64,
323
324 pub vendor_id: String,
325
326 pub brand: String,
327}
328
329#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
330#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
331pub struct CryptoHardware {
332 pub aesni: bool,
334
335 pub avx2: bool,
337
338 pub smt_logical_processor_count: Vec<u32>,
340
341 pub osxsave: bool,
343
344 pub sgx: bool,
346
347 pub xsave: bool,
349}
350
351#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
352#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
353pub struct NodeDescription {
354 pub moniker: String,
356
357 pub website: String,
359
360 pub security_contact: String,
362
363 pub details: String,
365}
366
367#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
369#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
370pub struct AuxiliaryDetailsV1 {
371 #[cfg_attr(feature = "openapi", schema(example = "PL", value_type = Option<String>))]
373 #[schemars(with = "Option<String>")]
374 #[schemars(length(equal = 2))]
375 pub location: Option<Country>,
376
377 #[serde(default)]
378 pub announce_ports: AnnouncePorts,
379
380 #[serde(default)]
384 pub accepted_operator_terms_and_conditions: bool,
385}
386
387impl From<AuxiliaryDetailsV2> for AuxiliaryDetailsV1 {
388 fn from(v2: AuxiliaryDetailsV2) -> Self {
389 Self {
390 location: v2.location,
391 announce_ports: v2.announce_ports,
392 accepted_operator_terms_and_conditions: v2.accepted_operator_terms_and_conditions,
393 }
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400
401 #[test]
402 fn legacy_host_information_deserialisation() {
403 let legacy_raw = r#"
404 {
405 "data": {
406 "ip_address": [
407 "194.182.184.55"
408 ],
409 "hostname": null,
410 "keys": {
411 "ed25519_identity": "2RMWm7PoadaoWpk3KhT2tcFFfA4oKUyC44KwmVvjxNDS",
412 "x25519_sphinx": "Awn4R2AHX91tYeiMJMxW3mFfoePuHWzZYUFdDQnydZCD",
413 "x25519_noise": null
414 }
415 },
416 "signature": "5JcXh766JANhz3bu2hMBS8onTLihQn6vnGgduJg1qd8JAcPGPbXBwBTKmmQPYCVGeZYFHW4CMGhfHVBu2A1rE5f7"
417 }
418 "#;
419
420 let res = serde_json::from_str::<SignedHostInformation>(legacy_raw);
421 assert!(res.is_ok());
422 }
423}