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