1use crate::models::{
5 DeclaredRolesV1, LewesProtocolDetailsV1, NymNodeDataV1, OffsetDateTimeJsonSchemaWrapper,
6};
7use crate::pagination::{PaginatedResponse, Pagination};
8use nym_crypto::asymmetric::ed25519::serde_helpers::bs58_ed25519_pubkey;
9use nym_crypto::asymmetric::x25519::serde_helpers::bs58_x25519_pubkey;
10use nym_crypto::asymmetric::{ed25519, x25519};
11use nym_mixnet_contract_common::nym_node::Role;
12use nym_mixnet_contract_common::reward_params::Performance;
13use nym_mixnet_contract_common::{EpochId, Interval, NodeId};
14use nym_noise_keys::VersionedNoiseKeyV1;
15use serde::{Deserialize, Serialize};
16use std::collections::HashMap;
17use std::net::IpAddr;
18use time::OffsetDateTime;
19use utoipa::ToSchema;
20
21#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
22pub struct SkimmedNodesWithMetadata {
23 pub nodes: Vec<SkimmedNodeV1>,
24 pub metadata: NodesResponseMetadata,
25}
26
27impl SkimmedNodesWithMetadata {
28 pub fn new(nodes: Vec<SkimmedNodeV1>, metadata: NodesResponseMetadata) -> Self {
29 SkimmedNodesWithMetadata { nodes, metadata }
30 }
31}
32
33#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
34pub struct SemiSkimmedNodesWithMetadata {
35 pub nodes: Vec<SemiSkimmedNodeV1>,
36 pub metadata: NodesResponseMetadata,
37}
38
39impl SemiSkimmedNodesWithMetadata {
40 pub fn new(nodes: Vec<SemiSkimmedNodeV1>, metadata: NodesResponseMetadata) -> Self {
41 SemiSkimmedNodesWithMetadata { nodes, metadata }
42 }
43}
44
45#[derive(
46 Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema, PartialEq,
47)]
48#[serde(rename_all = "kebab-case")]
49pub enum TopologyRequestStatus {
50 NoUpdates,
51 Fresh(Interval),
52}
53
54#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
55pub struct CachedNodesResponse<T: ToSchema> {
56 pub refreshed_at: OffsetDateTimeJsonSchemaWrapper,
57 pub nodes: Vec<T>,
58}
59
60impl<T: ToSchema> From<Vec<T>> for CachedNodesResponse<T> {
61 fn from(nodes: Vec<T>) -> Self {
62 CachedNodesResponse::new(nodes)
63 }
64}
65
66impl<T: ToSchema> CachedNodesResponse<T> {
67 pub fn new(nodes: Vec<T>) -> Self {
68 CachedNodesResponse {
69 refreshed_at: OffsetDateTime::now_utc().into(),
70 nodes,
71 }
72 }
73}
74
75#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
76pub struct NodesResponseMetadata {
77 pub status: Option<TopologyRequestStatus>,
78 #[schema(value_type = u32)]
79 pub absolute_epoch_id: EpochId,
80 pub rotation_id: u32,
81 pub refreshed_at: OffsetDateTimeJsonSchemaWrapper,
82}
83
84impl NodesResponseMetadata {
85 pub fn consistency_check(&self, other: &NodesResponseMetadata) -> bool {
86 self.status == other.status
87 && self.absolute_epoch_id == other.absolute_epoch_id
88 && self.rotation_id == other.rotation_id
89 }
90
91 pub fn refreshed_at(&self) -> OffsetDateTime {
92 self.refreshed_at.into()
93 }
94}
95
96#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
97pub struct PaginatedCachedNodesResponseV1<T> {
100 pub status: Option<TopologyRequestStatus>,
101 pub refreshed_at: OffsetDateTimeJsonSchemaWrapper,
102 pub nodes: PaginatedResponse<T>,
103}
104
105#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema)]
106pub struct PaginatedCachedNodesResponseV2<T> {
107 pub metadata: NodesResponseMetadata,
108 pub nodes: PaginatedResponse<T>,
109}
110
111impl<T> From<PaginatedCachedNodesResponseV2<T>> for PaginatedCachedNodesResponseV1<T> {
112 fn from(res: PaginatedCachedNodesResponseV2<T>) -> Self {
113 PaginatedCachedNodesResponseV1 {
114 status: res.metadata.status,
115 refreshed_at: res.metadata.refreshed_at,
116 nodes: res.nodes,
117 }
118 }
119}
120
121impl<T> PaginatedCachedNodesResponseV2<T> {
122 pub fn new_full(
123 absolute_epoch_id: EpochId,
124 rotation_id: u32,
125 refreshed_at: impl Into<OffsetDateTimeJsonSchemaWrapper>,
126 nodes: Vec<T>,
127 ) -> Self {
128 PaginatedCachedNodesResponseV2 {
129 nodes: PaginatedResponse {
130 pagination: Pagination {
131 total: nodes.len(),
132 page: 0,
133 size: nodes.len(),
134 },
135 data: nodes,
136 },
137 metadata: NodesResponseMetadata {
138 refreshed_at: refreshed_at.into(),
139 status: None,
140 absolute_epoch_id,
141 rotation_id,
142 },
143 }
144 }
145
146 pub fn fresh(mut self, interval: Interval) -> Self {
147 self.metadata.status = Some(TopologyRequestStatus::Fresh(interval));
148 self
149 }
150
151 pub fn no_updates(absolute_epoch_id: EpochId, rotation_id: u32) -> Self {
152 PaginatedCachedNodesResponseV2 {
153 nodes: PaginatedResponse {
154 pagination: Pagination {
155 total: 0,
156 page: 0,
157 size: 0,
158 },
159 data: Vec::new(),
160 },
161 metadata: NodesResponseMetadata {
162 refreshed_at: OffsetDateTime::now_utc().into(),
163 status: Some(TopologyRequestStatus::NoUpdates),
164 absolute_epoch_id,
165 rotation_id,
166 },
167 }
168 }
169}
170
171#[derive(Clone, Copy, Debug, Serialize, Deserialize, schemars::JsonSchema, utoipa::ToSchema)]
172#[serde(rename_all = "kebab-case")]
173pub enum NodeRoleQueryParam {
174 ActiveMixnode,
175
176 #[serde(alias = "entry", alias = "gateway")]
177 EntryGateway,
178
179 #[serde(alias = "exit")]
180 ExitGateway,
181}
182
183#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema, Default)]
184pub enum NodeRole {
185 Mixnode {
187 layer: u8,
188 },
189
190 #[serde(alias = "entry", alias = "gateway")]
191 EntryGateway,
192
193 #[serde(alias = "exit")]
194 ExitGateway,
195
196 Standby,
198
199 #[default]
200 Inactive,
201}
202
203impl NodeRole {
204 pub fn is_inactive(&self) -> bool {
205 matches!(self, NodeRole::Inactive)
206 }
207}
208
209impl From<Option<Role>> for NodeRole {
210 fn from(role: Option<Role>) -> Self {
211 match role {
212 Some(Role::EntryGateway) => NodeRole::EntryGateway,
213 Some(Role::Layer1) => NodeRole::Mixnode { layer: 1 },
214 Some(Role::Layer2) => NodeRole::Mixnode { layer: 2 },
215 Some(Role::Layer3) => NodeRole::Mixnode { layer: 3 },
216 Some(Role::ExitGateway) => NodeRole::ExitGateway,
217 Some(Role::Standby) => NodeRole::Standby,
218 None => NodeRole::Inactive,
219 }
220 }
221}
222
223#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
224pub struct BasicEntryInformation {
225 pub hostname: Option<String>,
226
227 pub ws_port: u16,
228 pub wss_port: Option<u16>,
229}
230
231#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
233pub struct SkimmedNodeV1 {
234 #[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: DeclaredRolesV1,
258
259 pub entry: Option<BasicEntryInformation>,
260
261 #[schema(value_type = String)]
263 pub performance: Performance,
264}
265
266impl SkimmedNodeV1 {
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 SemiSkimmedNodeV1 {
279 pub basic: SkimmedNodeV1,
280
281 pub x25519_noise_versioned_key: Option<VersionedNoiseKeyV1>,
282 }
284
285#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
286pub struct FullFatNode {
287 pub expanded: SemiSkimmedNodeV1,
288
289 pub self_described: Option<NymNodeDataV1>,
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}
304
305#[derive(Clone, Debug, Serialize, Deserialize, schemars::JsonSchema, ToSchema)]
307pub struct SemiSkimmedNodeV3 {
308 pub basic: SkimmedNodeV1,
310
311 pub noise_key: Option<VersionedNoiseKeyV1>,
313
314 pub build_version: String,
316
317 pub lp: Option<LewesProtocolDetailsV1>,
319}