1use crate::fragment::{linked_fragment_payload_max_len, unlinked_fragment_payload_max_len};
5use fragment::FragmentHeader;
6use nym_crypto::asymmetric::ed25519::PublicKey;
7use serde::Serialize;
8pub use set::split_into_sets;
9use thiserror::Error;
10use utoipa::ToSchema;
11
12pub const MIN_PADDING_OVERHEAD: usize = 1;
13
14pub mod fragment;
26pub mod reconstruction;
27pub mod set;
28
29pub mod monitoring {
30 use crate::fragment::Fragment;
31 use crate::{ReceivedFragment, SentFragment};
32 use dashmap::DashMap;
33 use nym_crypto::asymmetric::ed25519::PublicKey;
34 use std::sync::LazyLock;
35 use std::sync::atomic::{AtomicBool, Ordering};
36
37 pub static ENABLED: AtomicBool = AtomicBool::new(false);
38
39 pub static FRAGMENTS_RECEIVED: LazyLock<DashMap<i32, Vec<ReceivedFragment>>> =
40 LazyLock::new(DashMap::new);
41
42 pub static FRAGMENTS_SENT: LazyLock<DashMap<i32, Vec<SentFragment>>> =
43 LazyLock::new(DashMap::new);
44
45 pub fn enable() {
46 ENABLED.store(true, Ordering::Relaxed)
47 }
48
49 pub fn enabled() -> bool {
50 ENABLED.load(Ordering::Relaxed)
51 }
52
53 #[macro_export]
54 macro_rules! now {
55 () => {
56 match std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH) {
57 Ok(n) => n.as_secs(),
58 Err(_) => 0,
59 }
60 };
61 }
62
63 pub fn fragment_received(fragment: &Fragment) {
64 if enabled() {
65 let id = fragment.fragment_identifier().set_id();
66 let mut entry = FRAGMENTS_RECEIVED.entry(id).or_default();
67 let r = ReceivedFragment::new(fragment.header(), now!());
68 entry.push(r);
69 }
70 }
71
72 pub fn fragment_sent(fragment: &Fragment, client_nonce: i32, destination: PublicKey) {
73 if enabled() {
74 let id = fragment.fragment_identifier().set_id();
75 let mut entry = FRAGMENTS_SENT.entry(id).or_default();
76 let s = SentFragment::new(fragment.header(), now!(), client_nonce, destination);
77 entry.push(s);
78 }
79 }
80}
81
82#[derive(Debug, Clone)]
83pub struct FragmentMixParams {
84 destination: PublicKey,
85}
86
87impl FragmentMixParams {
88 pub fn destination(&self) -> PublicKey {
89 self.destination
90 }
91}
92
93#[derive(Debug, Clone, Serialize, ToSchema)]
94pub struct SentFragment {
95 header: FragmentHeader,
96 at: u64,
97 client_nonce: i32,
98 #[serde(skip)]
99 mixnet_params: FragmentMixParams,
100}
101
102impl SentFragment {
103 fn new(header: FragmentHeader, at: u64, client_nonce: i32, destination: PublicKey) -> Self {
104 let mixnet_params = FragmentMixParams { destination };
105 SentFragment {
106 header,
107 at,
108 client_nonce,
109 mixnet_params,
110 }
111 }
112
113 pub fn header(&self) -> FragmentHeader {
114 self.header.clone()
115 }
116
117 pub fn at(&self) -> u64 {
118 self.at
119 }
120
121 pub fn client_nonce(&self) -> i32 {
122 self.client_nonce
123 }
124
125 pub fn seed(&self) -> i32 {
126 self.header().seed().wrapping_mul(self.client_nonce())
127 }
128
129 pub fn mixnet_params(&self) -> FragmentMixParams {
130 self.mixnet_params.clone()
131 }
132}
133
134#[derive(Debug, Clone, Serialize, ToSchema)]
135pub struct ReceivedFragment {
136 header: FragmentHeader,
137 at: u64,
138}
139
140impl ReceivedFragment {
141 fn new(header: FragmentHeader, at: u64) -> Self {
142 ReceivedFragment { header, at }
143 }
144
145 pub fn header(&self) -> FragmentHeader {
146 self.header.clone()
147 }
148
149 pub fn at(&self) -> u64 {
150 self.at
151 }
152}
153
154#[derive(PartialEq, Eq, Debug, Error)]
181pub enum ChunkingError {
182 #[error("Received payload is too long. Got {received}, expected {expected}")]
183 InvalidPayloadLengthError { received: usize, expected: usize },
184
185 #[error("Received payload is too long. Got {received}, expected at most {expected_at_most}")]
186 TooLongPayloadLengthError {
187 received: usize,
188 expected_at_most: usize,
189 },
190
191 #[error("Provided header was malformed or contained self-contradicting fields")]
193 MalformedHeaderError,
194
195 #[error(
196 "Received too few bytes to deserialize fragment header. Got {received}, expected {expected}"
197 )]
198 TooShortFragmentHeader { received: usize, expected: usize },
199
200 #[error("Received fragment identifier ({received}) is not a valid value!")]
201 MalformedFragmentIdentifier { received: i32 },
202}
203
204pub fn number_of_required_fragments(
207 message_len: usize,
208 plaintext_per_fragment: usize,
209) -> (usize, usize) {
210 let max_unlinked = unlinked_fragment_payload_max_len(plaintext_per_fragment);
211 let max_linked = linked_fragment_payload_max_len(plaintext_per_fragment);
212
213 match set::total_number_of_sets(message_len, plaintext_per_fragment) {
214 1 => {
215 if message_len < max_unlinked {
217 return (1, max_unlinked - message_len);
218 }
219
220 let quot = message_len / max_unlinked;
222 let rem = message_len % max_unlinked;
223
224 if rem == 0 {
225 (quot, 0)
226 } else {
227 (quot + 1, max_unlinked - rem)
228 }
229 }
230
231 n => {
232 let without_last = (n - 1) * (u8::MAX as usize);
240 let linked_fragments_without_last = (2 * n - 2) - 1;
241 let unlinked_fragments_without_last = without_last - linked_fragments_without_last;
242
243 let final_set_message_len = message_len
244 - linked_fragments_without_last * max_linked
245 - unlinked_fragments_without_last * max_unlinked;
246
247 match final_set_message_len {
250 n if n < max_linked => (without_last + 1, max_linked - final_set_message_len),
251 n if n == max_linked => (without_last + 1, 0),
252 _ => {
253 let remaining_len = final_set_message_len - max_linked;
254
255 let quot = remaining_len / max_unlinked;
256 let rem = remaining_len % max_unlinked;
257
258 if rem == 0 {
259 (without_last + quot + 1, 0)
260 } else {
261 (without_last + quot + 2, max_unlinked - rem)
262 }
263 }
264 }
265 }
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272 use crate::set::{max_one_way_linked_set_payload_length, two_way_linked_set_payload_length};
273 use nym_sphinx_addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
274 use nym_sphinx_params::packet_sizes::PacketSize;
275
276 #[test]
277 fn calculating_number_of_required_fragments() {
278 let used_plaintext_len = PacketSize::default().plaintext_size()
281 - PacketSize::AckPacket.size()
282 - MAX_NODE_ADDRESS_UNPADDED_LEN;
283
284 let plaintext_lens = vec![17, used_plaintext_len, 20, 42, 10000];
285 const SET_LEN: usize = u8::MAX as usize;
286
287 for plaintext_len in plaintext_lens {
288 let unlinked_len = unlinked_fragment_payload_max_len(plaintext_len);
289 let linked_len = linked_fragment_payload_max_len(plaintext_len);
290 let full_edge_set = max_one_way_linked_set_payload_length(plaintext_len);
291 let full_middle_set = two_way_linked_set_payload_length(plaintext_len);
292
293 let single_non_full_frag_message_len = unlinked_len - 5;
294 let (frags, space_left) =
295 number_of_required_fragments(single_non_full_frag_message_len, plaintext_len);
296 assert_eq!(frags, 1);
297 assert_eq!(space_left, unlinked_len - single_non_full_frag_message_len);
298
299 let single_full_frag_message_len = unlinked_len;
300 let (frags, space_left) =
301 number_of_required_fragments(single_full_frag_message_len, plaintext_len);
302 assert_eq!(frags, 1);
303 assert_eq!(space_left, 0);
304
305 let two_non_full_frags_len = unlinked_len + 1;
306 let (frags, space_left) =
307 number_of_required_fragments(two_non_full_frags_len, plaintext_len);
308 assert_eq!(frags, 2);
309 assert_eq!(space_left, unlinked_len - 1);
310
311 let two_full_frags_len = 2 * unlinked_len;
312 let (frags, space_left) =
313 number_of_required_fragments(two_full_frags_len, plaintext_len);
314 assert_eq!(frags, 2);
315 assert_eq!(space_left, 0);
316
317 let multi_single_set_frags_non_full = unlinked_len * 42 - 5;
318 let (frags, space_left) =
319 number_of_required_fragments(multi_single_set_frags_non_full, plaintext_len);
320 assert_eq!(frags, 42);
321 assert_eq!(space_left, 5);
322
323 let multi_single_set_frags_full = unlinked_len * 42;
324 let (frags, space_left) =
325 number_of_required_fragments(multi_single_set_frags_full, plaintext_len);
326 assert_eq!(frags, 42);
327 assert_eq!(space_left, 0);
328
329 let two_set_one_non_full_frag = full_edge_set + linked_len - 1;
330 let (frags, space_left) =
331 number_of_required_fragments(two_set_one_non_full_frag, plaintext_len);
332 assert_eq!(frags, SET_LEN + 1);
333 assert_eq!(space_left, 1);
334
335 let two_set_one_full_frag = full_edge_set + linked_len;
336 let (frags, space_left) =
337 number_of_required_fragments(two_set_one_full_frag, plaintext_len);
338 assert_eq!(frags, SET_LEN + 1);
339 assert_eq!(space_left, 0);
340
341 let two_set_multi_frags_non_full = full_edge_set + linked_len + unlinked_len * 41 - 5;
342 let (frags, space_left) =
343 number_of_required_fragments(two_set_multi_frags_non_full, plaintext_len);
344 assert_eq!(frags, SET_LEN + 42);
345 assert_eq!(space_left, 5);
346
347 let two_set_multi_frags_full = full_edge_set + linked_len + unlinked_len * 41;
348 let (frags, space_left) =
349 number_of_required_fragments(two_set_multi_frags_full, plaintext_len);
350 assert_eq!(frags, SET_LEN + 42);
351 assert_eq!(space_left, 0);
352
353 let ten_set_one_non_full_frag = full_edge_set + 8 * full_middle_set + linked_len - 1;
354 let (frags, space_left) =
355 number_of_required_fragments(ten_set_one_non_full_frag, plaintext_len);
356 assert_eq!(frags, 9 * SET_LEN + 1);
357 assert_eq!(space_left, 1);
358
359 let ten_set_one_full_frag = full_edge_set + 8 * full_middle_set + linked_len;
360 let (frags, space_left) =
361 number_of_required_fragments(ten_set_one_full_frag, plaintext_len);
362 assert_eq!(frags, 9 * SET_LEN + 1);
363 assert_eq!(space_left, 0);
364
365 let ten_set_multi_frags_non_full =
366 full_edge_set + 8 * full_middle_set + linked_len + 41 * unlinked_len - 5;
367 let (frags, space_left) =
368 number_of_required_fragments(ten_set_multi_frags_non_full, plaintext_len);
369 assert_eq!(frags, 9 * SET_LEN + 42);
370 assert_eq!(space_left, 5);
371
372 let ten_set_multi_frags_full =
373 full_edge_set + 8 * full_middle_set + linked_len + 41 * unlinked_len;
374 let (frags, space_left) =
375 number_of_required_fragments(ten_set_multi_frags_full, plaintext_len);
376 assert_eq!(frags, 9 * SET_LEN + 42);
377 assert_eq!(space_left, 0);
378 }
379 }
380}