use crate::fragment::{linked_fragment_payload_max_len, unlinked_fragment_payload_max_len};
use fragment::FragmentHeader;
use nym_crypto::asymmetric::ed25519::PublicKey;
use serde::Serialize;
pub use set::split_into_sets;
use thiserror::Error;
use utoipa::ToSchema;
pub const MIN_PADDING_OVERHEAD: usize = 1;
pub mod fragment;
pub mod reconstruction;
pub mod set;
pub mod monitoring {
use crate::fragment::Fragment;
use crate::{ReceivedFragment, SentFragment};
use dashmap::DashMap;
use nym_crypto::asymmetric::ed25519::PublicKey;
use std::sync::LazyLock;
use std::sync::atomic::{AtomicBool, Ordering};
pub static ENABLED: AtomicBool = AtomicBool::new(false);
pub static FRAGMENTS_RECEIVED: LazyLock<DashMap<i32, Vec<ReceivedFragment>>> =
LazyLock::new(DashMap::new);
pub static FRAGMENTS_SENT: LazyLock<DashMap<i32, Vec<SentFragment>>> =
LazyLock::new(DashMap::new);
pub fn enable() {
ENABLED.store(true, Ordering::Relaxed)
}
pub fn enabled() -> bool {
ENABLED.load(Ordering::Relaxed)
}
#[macro_export]
macro_rules! now {
() => {
match std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH) {
Ok(n) => n.as_secs(),
Err(_) => 0,
}
};
}
pub fn fragment_received(fragment: &Fragment) {
if enabled() {
let id = fragment.fragment_identifier().set_id();
let mut entry = FRAGMENTS_RECEIVED.entry(id).or_default();
let r = ReceivedFragment::new(fragment.header(), now!());
entry.push(r);
}
}
pub fn fragment_sent(fragment: &Fragment, client_nonce: i32, destination: PublicKey) {
if enabled() {
let id = fragment.fragment_identifier().set_id();
let mut entry = FRAGMENTS_SENT.entry(id).or_default();
let s = SentFragment::new(fragment.header(), now!(), client_nonce, destination);
entry.push(s);
}
}
}
#[derive(Debug, Clone)]
pub struct FragmentMixParams {
destination: PublicKey,
}
impl FragmentMixParams {
pub fn destination(&self) -> PublicKey {
self.destination
}
}
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct SentFragment {
header: FragmentHeader,
at: u64,
client_nonce: i32,
#[serde(skip)]
mixnet_params: FragmentMixParams,
}
impl SentFragment {
fn new(header: FragmentHeader, at: u64, client_nonce: i32, destination: PublicKey) -> Self {
let mixnet_params = FragmentMixParams { destination };
SentFragment {
header,
at,
client_nonce,
mixnet_params,
}
}
pub fn header(&self) -> FragmentHeader {
self.header.clone()
}
pub fn at(&self) -> u64 {
self.at
}
pub fn client_nonce(&self) -> i32 {
self.client_nonce
}
pub fn seed(&self) -> i32 {
self.header().seed().wrapping_mul(self.client_nonce())
}
pub fn mixnet_params(&self) -> FragmentMixParams {
self.mixnet_params.clone()
}
}
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct ReceivedFragment {
header: FragmentHeader,
at: u64,
}
impl ReceivedFragment {
fn new(header: FragmentHeader, at: u64) -> Self {
ReceivedFragment { header, at }
}
pub fn header(&self) -> FragmentHeader {
self.header.clone()
}
pub fn at(&self) -> u64 {
self.at
}
}
#[derive(PartialEq, Eq, Debug, Error)]
pub enum ChunkingError {
#[error("Received payload is too long. Got {received}, expected {expected}")]
InvalidPayloadLengthError { received: usize, expected: usize },
#[error("Received payload is too long. Got {received}, expected at most {expected_at_most}")]
TooLongPayloadLengthError {
received: usize,
expected_at_most: usize,
},
#[error("Provided header was malformed or contained self-contradicting fields")]
MalformedHeaderError,
#[error(
"Received too few bytes to deserialize fragment header. Got {received}, expected {expected}"
)]
TooShortFragmentHeader { received: usize, expected: usize },
#[error("Received fragment identifier ({received}) is not a valid value!")]
MalformedFragmentIdentifier { received: i32 },
}
pub fn number_of_required_fragments(
message_len: usize,
plaintext_per_fragment: usize,
) -> (usize, usize) {
let max_unlinked = unlinked_fragment_payload_max_len(plaintext_per_fragment);
let max_linked = linked_fragment_payload_max_len(plaintext_per_fragment);
match set::total_number_of_sets(message_len, plaintext_per_fragment) {
1 => {
if message_len < max_unlinked {
return (1, max_unlinked - message_len);
}
let quot = message_len / max_unlinked;
let rem = message_len % max_unlinked;
if rem == 0 {
(quot, 0)
} else {
(quot + 1, max_unlinked - rem)
}
}
n => {
let without_last = (n - 1) * (u8::MAX as usize);
let linked_fragments_without_last = (2 * n - 2) - 1;
let unlinked_fragments_without_last = without_last - linked_fragments_without_last;
let final_set_message_len = message_len
- linked_fragments_without_last * max_linked
- unlinked_fragments_without_last * max_unlinked;
match final_set_message_len {
n if n < max_linked => (without_last + 1, max_linked - final_set_message_len),
n if n == max_linked => (without_last + 1, 0),
_ => {
let remaining_len = final_set_message_len - max_linked;
let quot = remaining_len / max_unlinked;
let rem = remaining_len % max_unlinked;
if rem == 0 {
(without_last + quot + 1, 0)
} else {
(without_last + quot + 2, max_unlinked - rem)
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::set::{max_one_way_linked_set_payload_length, two_way_linked_set_payload_length};
use nym_sphinx_addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
use nym_sphinx_params::packet_sizes::PacketSize;
#[test]
fn calculating_number_of_required_fragments() {
let used_plaintext_len = PacketSize::default().plaintext_size()
- PacketSize::AckPacket.size()
- MAX_NODE_ADDRESS_UNPADDED_LEN;
let plaintext_lens = vec![17, used_plaintext_len, 20, 42, 10000];
const SET_LEN: usize = u8::MAX as usize;
for plaintext_len in plaintext_lens {
let unlinked_len = unlinked_fragment_payload_max_len(plaintext_len);
let linked_len = linked_fragment_payload_max_len(plaintext_len);
let full_edge_set = max_one_way_linked_set_payload_length(plaintext_len);
let full_middle_set = two_way_linked_set_payload_length(plaintext_len);
let single_non_full_frag_message_len = unlinked_len - 5;
let (frags, space_left) =
number_of_required_fragments(single_non_full_frag_message_len, plaintext_len);
assert_eq!(frags, 1);
assert_eq!(space_left, unlinked_len - single_non_full_frag_message_len);
let single_full_frag_message_len = unlinked_len;
let (frags, space_left) =
number_of_required_fragments(single_full_frag_message_len, plaintext_len);
assert_eq!(frags, 1);
assert_eq!(space_left, 0);
let two_non_full_frags_len = unlinked_len + 1;
let (frags, space_left) =
number_of_required_fragments(two_non_full_frags_len, plaintext_len);
assert_eq!(frags, 2);
assert_eq!(space_left, unlinked_len - 1);
let two_full_frags_len = 2 * unlinked_len;
let (frags, space_left) =
number_of_required_fragments(two_full_frags_len, plaintext_len);
assert_eq!(frags, 2);
assert_eq!(space_left, 0);
let multi_single_set_frags_non_full = unlinked_len * 42 - 5;
let (frags, space_left) =
number_of_required_fragments(multi_single_set_frags_non_full, plaintext_len);
assert_eq!(frags, 42);
assert_eq!(space_left, 5);
let multi_single_set_frags_full = unlinked_len * 42;
let (frags, space_left) =
number_of_required_fragments(multi_single_set_frags_full, plaintext_len);
assert_eq!(frags, 42);
assert_eq!(space_left, 0);
let two_set_one_non_full_frag = full_edge_set + linked_len - 1;
let (frags, space_left) =
number_of_required_fragments(two_set_one_non_full_frag, plaintext_len);
assert_eq!(frags, SET_LEN + 1);
assert_eq!(space_left, 1);
let two_set_one_full_frag = full_edge_set + linked_len;
let (frags, space_left) =
number_of_required_fragments(two_set_one_full_frag, plaintext_len);
assert_eq!(frags, SET_LEN + 1);
assert_eq!(space_left, 0);
let two_set_multi_frags_non_full = full_edge_set + linked_len + unlinked_len * 41 - 5;
let (frags, space_left) =
number_of_required_fragments(two_set_multi_frags_non_full, plaintext_len);
assert_eq!(frags, SET_LEN + 42);
assert_eq!(space_left, 5);
let two_set_multi_frags_full = full_edge_set + linked_len + unlinked_len * 41;
let (frags, space_left) =
number_of_required_fragments(two_set_multi_frags_full, plaintext_len);
assert_eq!(frags, SET_LEN + 42);
assert_eq!(space_left, 0);
let ten_set_one_non_full_frag = full_edge_set + 8 * full_middle_set + linked_len - 1;
let (frags, space_left) =
number_of_required_fragments(ten_set_one_non_full_frag, plaintext_len);
assert_eq!(frags, 9 * SET_LEN + 1);
assert_eq!(space_left, 1);
let ten_set_one_full_frag = full_edge_set + 8 * full_middle_set + linked_len;
let (frags, space_left) =
number_of_required_fragments(ten_set_one_full_frag, plaintext_len);
assert_eq!(frags, 9 * SET_LEN + 1);
assert_eq!(space_left, 0);
let ten_set_multi_frags_non_full =
full_edge_set + 8 * full_middle_set + linked_len + 41 * unlinked_len - 5;
let (frags, space_left) =
number_of_required_fragments(ten_set_multi_frags_non_full, plaintext_len);
assert_eq!(frags, 9 * SET_LEN + 42);
assert_eq!(space_left, 5);
let ten_set_multi_frags_full =
full_edge_set + 8 * full_middle_set + linked_len + 41 * unlinked_len;
let (frags, space_left) =
number_of_required_fragments(ten_set_multi_frags_full, plaintext_len);
assert_eq!(frags, 9 * SET_LEN + 42);
assert_eq!(space_left, 0);
}
}
}