1use crate::models::{DeclaredRoles, NymNodeData, OffsetDateTimeJsonSchemaWrapper};
5use crate::pagination::{PaginatedResponse, Pagination};
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::nym_node::Role;
10use nym_mixnet_contract_common::reward_params::Performance;
11use nym_mixnet_contract_common::{EpochId, Interval, NodeId};
12use nym_noise_keys::VersionedNoiseKey;
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::net::IpAddr;
16use time::OffsetDateTime;
17use utoipa::ToSchema;
18
19#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
20pub struct SkimmedNodesWithMetadata {
21 pub nodes: Vec<SkimmedNode>,
22 pub metadata: NodesResponseMetadata,
23}
24
25impl SkimmedNodesWithMetadata {
26 pub fn new(nodes: Vec<SkimmedNode>, metadata: NodesResponseMetadata) -> Self {
27 SkimmedNodesWithMetadata { nodes, metadata }
28 }
29}
30
31#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
32pub struct SemiSkimmedNodesWithMetadata {
33 pub nodes: Vec<SemiSkimmedNode>,
34 pub metadata: NodesResponseMetadata,
35}
36
37impl SemiSkimmedNodesWithMetadata {
38 pub fn new(nodes: Vec<SemiSkimmedNode>, metadata: NodesResponseMetadata) -> Self {
39 SemiSkimmedNodesWithMetadata { nodes, metadata }
40 }
41}
42
43#[derive(
44 Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema, PartialEq,
45)]
46#[serde(rename_all = "kebab-case")]
47pub enum TopologyRequestStatus {
48 NoUpdates,
49 Fresh(Interval),
50}
51
52#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
53pub struct CachedNodesResponse<T: ToSchema> {
54 pub refreshed_at: OffsetDateTimeJsonSchemaWrapper,
55 pub nodes: Vec<T>,
56}
57
58impl<T: ToSchema> From<Vec<T>> for CachedNodesResponse<T> {
59 fn from(nodes: Vec<T>) -> Self {
60 CachedNodesResponse::new(nodes)
61 }
62}
63
64impl<T: ToSchema> CachedNodesResponse<T> {
65 pub fn new(nodes: Vec<T>) -> Self {
66 CachedNodesResponse {
67 refreshed_at: OffsetDateTime::now_utc().into(),
68 nodes,
69 }
70 }
71}
72
73#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
74pub struct NodesResponseMetadata {
75 pub status: Option<TopologyRequestStatus>,
76 #[schema(value_type = u32)]
77 pub absolute_epoch_id: EpochId,
78 pub rotation_id: u32,
79 pub refreshed_at: OffsetDateTimeJsonSchemaWrapper,
80}
81
82impl NodesResponseMetadata {
83 pub fn consistency_check(&self, other: &NodesResponseMetadata) -> bool {
84 self.status == other.status
85 && self.absolute_epoch_id == other.absolute_epoch_id
86 && self.rotation_id == other.rotation_id
87 }
88
89 pub fn refreshed_at(&self) -> OffsetDateTime {
90 self.refreshed_at.into()
91 }
92}
93
94#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
95pub struct PaginatedCachedNodesResponseV1<T> {
98 pub status: Option<TopologyRequestStatus>,
99 pub refreshed_at: OffsetDateTimeJsonSchemaWrapper,
100 pub nodes: PaginatedResponse<T>,
101}
102
103#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
104pub struct PaginatedCachedNodesResponseV2<T> {
105 pub metadata: NodesResponseMetadata,
106 pub nodes: PaginatedResponse<T>,
107}
108
109impl<T> From<PaginatedCachedNodesResponseV2<T>> for PaginatedCachedNodesResponseV1<T> {
110 fn from(res: PaginatedCachedNodesResponseV2<T>) -> Self {
111 PaginatedCachedNodesResponseV1 {
112 status: res.metadata.status,
113 refreshed_at: res.metadata.refreshed_at,
114 nodes: res.nodes,
115 }
116 }
117}
118
119impl<T> PaginatedCachedNodesResponseV2<T> {
120 pub fn new_full(
121 absolute_epoch_id: EpochId,
122 rotation_id: u32,
123 refreshed_at: impl Into<OffsetDateTimeJsonSchemaWrapper>,
124 nodes: Vec<T>,
125 ) -> Self {
126 PaginatedCachedNodesResponseV2 {
127 nodes: PaginatedResponse {
128 pagination: Pagination {
129 total: nodes.len(),
130 page: 0,
131 size: nodes.len(),
132 },
133 data: nodes,
134 },
135 metadata: NodesResponseMetadata {
136 refreshed_at: refreshed_at.into(),
137 status: None,
138 absolute_epoch_id,
139 rotation_id,
140 },
141 }
142 }
143
144 pub fn fresh(mut self, interval: Interval) -> Self {
145 self.metadata.status = Some(TopologyRequestStatus::Fresh(interval));
146 self
147 }
148
149 pub fn no_updates(absolute_epoch_id: EpochId, rotation_id: u32) -> Self {
150 PaginatedCachedNodesResponseV2 {
151 nodes: PaginatedResponse {
152 pagination: Pagination {
153 total: 0,
154 page: 0,
155 size: 0,
156 },
157 data: Vec::new(),
158 },
159 metadata: NodesResponseMetadata {
160 refreshed_at: OffsetDateTime::now_utc().into(),
161 status: Some(TopologyRequestStatus::NoUpdates),
162 absolute_epoch_id,
163 rotation_id,
164 },
165 }
166 }
167}
168
169#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
170#[serde(rename_all = "kebab-case")]
171pub enum NodeRoleQueryParam {
172 ActiveMixnode,
173
174 #[serde(alias = "entry", alias = "gateway")]
175 EntryGateway,
176
177 #[serde(alias = "exit")]
178 ExitGateway,
179}
180
181#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema, Default)]
182pub enum NodeRole {
183 Mixnode {
185 layer: u8,
186 },
187
188 #[serde(alias = "entry", alias = "gateway")]
189 EntryGateway,
190
191 #[serde(alias = "exit")]
192 ExitGateway,
193
194 Standby,
196
197 #[default]
198 Inactive,
199}
200
201impl NodeRole {
202 pub fn is_inactive(&self) -> bool {
203 matches!(self, NodeRole::Inactive)
204 }
205}
206
207impl From<Option<Role>> for NodeRole {
208 fn from(role: Option<Role>) -> Self {
209 match role {
210 Some(Role::EntryGateway) => NodeRole::EntryGateway,
211 Some(Role::Layer1) => NodeRole::Mixnode { layer: 1 },
212 Some(Role::Layer2) => NodeRole::Mixnode { layer: 2 },
213 Some(Role::Layer3) => NodeRole::Mixnode { layer: 3 },
214 Some(Role::ExitGateway) => NodeRole::ExitGateway,
215 Some(Role::Standby) => NodeRole::Standby,
216 None => NodeRole::Inactive,
217 }
218 }
219}
220
221#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
222pub struct BasicEntryInformation {
223 pub hostname: Option<String>,
224
225 pub ws_port: u16,
226 pub wss_port: Option<u16>,
227}
228
229#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
231pub struct SkimmedNode {
232 #[schema(value_type = u32)]
235 pub node_id: NodeId,
236
237 #[serde(with = "bs58_ed25519_pubkey")]
238 #[schemars(with = "String")]
239 #[schema(value_type = String)]
240 pub ed25519_identity_pubkey: ed25519::PublicKey,
241
242 #[schema(value_type = Vec<String>)]
243 pub ip_addresses: Vec<IpAddr>,
244
245 pub mix_port: u16,
246
247 #[serde(with = "bs58_x25519_pubkey")]
248 #[schemars(with = "String")]
249 #[schema(value_type = String)]
250 pub x25519_sphinx_pubkey: x25519::PublicKey,
251
252 #[serde(alias = "epoch_role")]
253 pub role: NodeRole,
254
255 #[serde(default)]
257 pub supported_roles: DeclaredRoles,
258
259 pub entry: Option<BasicEntryInformation>,
260
261 #[schema(value_type = String)]
263 pub performance: Performance,
264}
265
266impl SkimmedNode {
267 pub fn get_mix_layer(&self) -> Option<u8> {
268 match self.role {
269 NodeRole::Mixnode { layer } => Some(layer),
270 _ => None,
271 }
272 }
273}
274
275#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
278pub struct SemiSkimmedNode {
279 pub basic: SkimmedNode,
280
281 pub x25519_noise_versioned_key: Option<VersionedNoiseKey>,
282 }
284
285#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
286pub struct FullFatNode {
287 pub expanded: SemiSkimmedNode,
288
289 pub self_described: Option<NymNodeData>,
291}
292
293#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, ToSchema)]
294pub struct NodesByAddressesRequestBody {
295 #[schema(value_type = Vec<String>)]
296 pub addresses: Vec<IpAddr>,
297}
298
299#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, ToSchema)]
300pub struct NodesByAddressesResponse {
301 #[schema(value_type = HashMap<String, Option<u32>>)]
302 pub existence: HashMap<IpAddr, Option<NodeId>>,
303}