Files
nym/common/nymsphinx/chunking/src/set.rs
T
Jędrzej Stuczyński e5afd54ce0 Feature/node status api (#680)
* Basic storage stub

* New models for new node status api

* Route handling

* Mounting new routes

* Missing selective commit

* Moved network monitor related files to separate package

* Starting to see some sqlx action

* Schema updates

* Log statement upon finished migration

* Removed old diesel related imports

* Converted mixnode cache initialisation into a fairing

* Moved cache related functionalities to separate package

Also defined staging there

* Created run method for validator cache + removed unwrap

* Removed old node-status-api types and left bunch of todo placeholders in their place

* Fixed managing validatorcache

* Status reports are starting to get constructed

* Submitting some dummy results to the database

* Removing duplicate code for generating reports

* Removed statuses older than 48h

* Initial attempt at trying to obtain reports for all active nodes

* Removed duplicates from the full report

* Grabbing uptime history

* Updating historical uptimes of active nodes

* Updated sqlx-data.json

* Removed all placeholder foomp owner values

* Changed Layer serde behaviour for easier usage

* Extended validator api config

* Initial (seems working !) integration with network monitor

* Added database path configuration to config

* Using ValidatorCache in NetworkMonitor

* Flag indicating whether validator cache has been initialised

* Introduced a locla-only route for reward script to perform daily chores

* Flag to save config to a file

* Moved spawning of receiving future to run method rather than new

* Removed arguments that dont make sense to be configured via CLI

* Removed dead code from config file

* More dead code removal

* Added validator API to CI

* Corrected manifest-path arguments

* Constructing network monitor by passing config

* Combined validator API CI with the main CI file

* Using query_as for NodeStatus

* Checking if historical uptimes were already calculated on particular day

* Making id field NOT NULL

* More query_as! action

* Updated sqlx-data.json

* Removed unused chrono feature

* Renamed the migration file

* Changed default validator endpoint to point to local validator

* Removing unnecessary clone

* More appropriate naming

* Removed dead code

* Lock file updates

* Updated network monitor address in contract code

* Don't stage node status api if network monitor is disabled

* cargo fmt

* Updated all license notices to SPDX
2021-07-19 14:02:47 +01:00

896 lines
35 KiB
Rust

// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::fragment::{
linked_fragment_payload_max_len, unlinked_fragment_payload_max_len, Fragment,
LINKED_FRAGMENTED_HEADER_LEN, UNLINKED_FRAGMENTED_HEADER_LEN,
};
use rand::Rng;
/// In the simplest case of message being divided into a single set, the set has the upper bound
/// on its payload length of the maximum number of `Fragment`s multiplied by their maximum,
/// fragmented, length.
pub const fn max_unlinked_set_payload_length(max_plaintext_size: usize) -> usize {
u8::max_value() as usize * unlinked_fragment_payload_max_len(max_plaintext_size)
}
/// If the set is being linked to another one, by either being the very first set, or the very last,
/// one of its `Fragment`s must be changed from "unlinked" into "linked" to compensate for a tiny
/// bit extra data overhead: id of the other set.
/// Note that the "MAX" prefix only applies to if the set is the last one as it does not have
/// a lower bound on its length. If the set is one way linked and a first one, it *must have*
/// this exact payload length instead.
pub const fn max_one_way_linked_set_payload_length(max_plaintext_size: usize) -> usize {
max_unlinked_set_payload_length(max_plaintext_size)
- (LINKED_FRAGMENTED_HEADER_LEN - UNLINKED_FRAGMENTED_HEADER_LEN)
}
/// If the set is being linked two others sets by being stuck in the middle of divided message,
/// two of its `Fragment`s (first and final one) must be changed from
/// "unlinked" into "linked" to compensate for data overhead.
/// Note that this constant no longer has a "MAX" prefix, this is because each set being stuck
/// between different sets, *must* have this exact payload length.
pub const fn two_way_linked_set_payload_length(max_plaintext_size: usize) -> usize {
max_unlinked_set_payload_length(max_plaintext_size)
- 2 * (LINKED_FRAGMENTED_HEADER_LEN - UNLINKED_FRAGMENTED_HEADER_LEN)
}
/// `FragmentSet` is an ordered collection of 1 to 255 `Fragment`s, each with the same ID
/// that can be used to produce original message, assuming no linking took place.
///
/// Otherwise, if set linking took place, then first or last `Fragment` from the `FragmentSet`
/// is used to determine preceding or succeeding other `FragmentSet`
/// that should be used in tandem to reconstruct original message. The linking reconstruction
/// is a recursive process as a message could have been divided into an arbitrary number of
/// `FragmentSet`s with no upper bound at all.
///
/// For example if a message was divided into 300 `Fragment`s (i.e. 2 `FragmentSet`s,
/// the structures might look as follows:
///
/// Set1: [f1 {id = 12345}, f2 {id = 12345}, ... f255 {id = 12345, next_id = 54321}]
/// Set2: [f1 {id = 54321, previous_id = 12345}, f2 {id = 54321}, ... f45 {id = 54321}]
pub(crate) type FragmentSet = Vec<Fragment>;
/// Generate a pseudo-random id for a `FragmentSet`.
/// Its value is restricted to (0, i32::max_value()].
/// Note that it *excludes* 0, but *includes* i32::max_value().
/// This particular range allows for the id to be represented using 31bits, rather than
/// the full length of 32 while still providing more than enough variability to
/// distinguish different `FragmentSet`s.
/// The extra bit, as explained in `Fragment` definition is used to represents additional information,
/// indicating how further bytes should be parsed.
/// This approach saves whole byte per `Fragment`, which while may seem insignificant and
/// introduces extra complexity, quickly adds up when faced with sphinx packet encapsulation for longer
/// messages.
/// Finally, the reason 0 id is not allowed is to explicitly distinguish it from `COVER_FRAG_ID`
/// `Fragment`s thus allowing for some additional optimizations by letting it skip
/// certain procedures when reconstructing.
pub(crate) fn generate_set_id<R: Rng>(rng: &mut R) -> i32 {
let potential_id = rng.gen::<i32>().abs();
// make sure id is always non-zero, as we do not want to accidentally have weird
// reconstruction cases where unfragmented payload overwrites some part of set with id0
if potential_id == 0 {
generate_set_id(rng)
} else {
potential_id
}
}
/// Splits underlying message into multiple `Fragment`s while all of them fit in a single
/// `Set` (number of `Fragment`s <= 255)
fn prepare_unlinked_fragmented_set(
message: &[u8],
id: i32,
max_plaintext_size: usize,
) -> FragmentSet {
let pre_casted_frags = (message.len() as f64
/ unlinked_fragment_payload_max_len(max_plaintext_size) as f64)
.ceil() as usize;
debug_assert!(pre_casted_frags <= u8::max_value() as usize);
let num_fragments = pre_casted_frags as u8;
let mut fragments = Vec::with_capacity(num_fragments as usize);
for i in 1..(pre_casted_frags + 1) {
// we can't use u8 directly here as upper (NON-INCLUSIVE, so it would always fit) bound could be u8::max_value() + 1
let lb = (i as usize - 1) * unlinked_fragment_payload_max_len(max_plaintext_size);
let ub = usize::min(
message.len(),
i as usize * unlinked_fragment_payload_max_len(max_plaintext_size),
);
fragments.push(
Fragment::try_new(
&message[lb..ub],
id,
num_fragments,
i as u8,
None,
None,
max_plaintext_size,
)
.unwrap(),
)
}
fragments
}
/// Similarly to `prepare_unlinked_fragmented_set`, splits part of underlying message into
/// multiple `Fragment`s. The byte slice of the message *must* fit into a single linked set, however,
/// the whole message itself is still longer than a single `Set` (number of `Fragment`s > 255).
/// During the process of splitting message, this function is called multiple times.
fn prepare_linked_fragment_set(
message: &[u8],
id: i32,
previous_link_id: Option<i32>,
next_link_id: Option<i32>,
max_plaintext_size: usize,
) -> FragmentSet {
// determine number of fragments in the set:
let num_frags_usize = if next_link_id.is_some() {
u8::max_value() as usize
} else {
// we know this set is linked, if it's not post-linked then it MUST BE pre-linked
let tail_len = if message.len() >= linked_fragment_payload_max_len(max_plaintext_size) {
message.len() - linked_fragment_payload_max_len(max_plaintext_size)
} else {
0
};
let pre_casted_frags = 1
+ (tail_len as f64 / unlinked_fragment_payload_max_len(max_plaintext_size) as f64)
.ceil() as usize;
if pre_casted_frags > u8::max_value() as usize {
panic!("message would produce too many fragments!")
};
pre_casted_frags
};
// determine bounds for the first fragment which depends on whether set is pre-linked
let mut lb = 0;
let mut ub = if previous_link_id.is_some() {
usize::min(
message.len(),
linked_fragment_payload_max_len(max_plaintext_size),
)
} else {
// the set might be linked, but fragment itself is not (i.e. the set is linked at the tail)
unlinked_fragment_payload_max_len(max_plaintext_size)
};
let mut fragments = Vec::with_capacity(num_frags_usize);
for i in 1..(num_frags_usize + 1) {
// we can't use u8 directly here as upper (NON-INCLUSIVE, so i would always fit) bound could be u8::max_value() + 1
let fragment = Fragment::try_new(
&message[lb..ub],
id,
num_frags_usize as u8,
i as u8,
if i == 1 { previous_link_id } else { None },
if i == num_frags_usize {
next_link_id
} else {
None
},
max_plaintext_size,
)
.unwrap();
fragments.push(fragment);
// update bounds for the next fragment
lb = ub;
ub = usize::min(
message.len(),
ub + unlinked_fragment_payload_max_len(max_plaintext_size),
);
}
fragments
}
/// Based on total message length, determines the number of sets into which it is going to be split.
pub(crate) fn total_number_of_sets(message_len: usize, max_plaintext_size: usize) -> usize {
if message_len <= max_unlinked_set_payload_length(max_plaintext_size) {
1
} else if message_len > max_unlinked_set_payload_length(max_plaintext_size)
&& message_len <= 2 * max_one_way_linked_set_payload_length(max_plaintext_size)
{
2
} else {
let len_without_edges =
message_len - 2 * max_one_way_linked_set_payload_length(max_plaintext_size);
// every set in between edges must be two way linked
(len_without_edges as f64 / two_way_linked_set_payload_length(max_plaintext_size) as f64)
.ceil() as usize
+ 2
}
}
/// Given part of the underlying message as well id of the set as well as its potential linked sets,
/// correctly delegates to appropriate set constructor.
fn prepare_fragment_set(
message: &[u8],
id: i32,
previous_link_id: Option<i32>,
next_link_id: Option<i32>,
max_plaintext_size: usize,
) -> FragmentSet {
if previous_link_id.is_some() || next_link_id.is_some() {
prepare_linked_fragment_set(
message,
id,
previous_link_id,
next_link_id,
max_plaintext_size,
)
} else {
// the bounds on whether the message fits in an unlinked set should have been done by the callee
// when determining ids of other sets
prepare_unlinked_fragmented_set(message, id, max_plaintext_size)
}
}
/// Entry point for splitting whole message into possibly multiple [`Set`]s.
// TODO: make it take message: Vec<u8> instead
pub fn split_into_sets<R: Rng>(
rng: &mut R,
message: &[u8],
max_plaintext_size: usize,
) -> Vec<FragmentSet> {
let num_of_sets = total_number_of_sets(message.len(), max_plaintext_size);
if num_of_sets == 1 {
let set_id = generate_set_id(rng);
vec![prepare_fragment_set(
message,
set_id,
None,
None,
max_plaintext_size,
)]
} else {
let mut sets = Vec::with_capacity(num_of_sets);
// pre-generate all ids for the sets
let set_ids: Vec<_> = std::iter::repeat(())
.map(|_| generate_set_id(rng))
.take(num_of_sets)
.collect();
// initial bounds for the set payloads
let mut lb = 0;
let mut ub = max_one_way_linked_set_payload_length(max_plaintext_size);
for i in 0..num_of_sets {
let fragment_set = prepare_fragment_set(
&message[lb..ub],
set_ids[i],
if i == 0 { None } else { Some(set_ids[i - 1]) },
if i == (num_of_sets - 1) {
None
} else {
Some(set_ids[i + 1])
},
max_plaintext_size,
);
sets.push(fragment_set);
// update bounds for the next set
lb = ub;
ub = if i == num_of_sets - 2 {
// we're going to go into the last iteration now, hence the last set will be one-way linked
usize::min(
message.len(),
ub + max_one_way_linked_set_payload_length(max_plaintext_size),
)
} else {
usize::min(
message.len(),
ub + two_way_linked_set_payload_length(max_plaintext_size),
)
}
}
sets
}
}
// reason for top level tests module is to be able to use the helper functions to verify sets payloads
#[cfg(test)]
mod tests {
use super::*;
use nymsphinx_params::packet_sizes::PacketSize;
fn max_plaintext_size() -> usize {
PacketSize::default().plaintext_size() - PacketSize::AckPacket.size()
}
fn verify_unlinked_set_payload(mut set: FragmentSet, payload: &[u8]) {
for i in (0..set.len()).rev() {
assert_eq!(
set.pop().unwrap().extract_payload(),
payload[i * unlinked_fragment_payload_max_len(max_plaintext_size())
..usize::min(
payload.len(),
(i + 1) * unlinked_fragment_payload_max_len(max_plaintext_size())
)]
.to_vec()
)
}
}
fn verify_pre_linked_set_payload(mut set: FragmentSet, payload: &[u8]) {
for i in (0..set.len()).rev() {
let lb = if i == 0 {
0
} else {
(i - 1) * unlinked_fragment_payload_max_len(max_plaintext_size())
+ linked_fragment_payload_max_len(max_plaintext_size())
};
let ub = usize::min(
payload.len(),
i * unlinked_fragment_payload_max_len(max_plaintext_size())
+ linked_fragment_payload_max_len(max_plaintext_size()),
);
assert_eq!(
set.pop().unwrap().extract_payload(),
payload[lb..ub].to_vec()
)
}
}
fn verify_post_linked_set_payload(mut set: FragmentSet, payload: &[u8]) {
for i in (0..set.len()).rev() {
let lb = i * unlinked_fragment_payload_max_len(max_plaintext_size());
let ub = if i == (u8::max_value() as usize - 1) {
i * unlinked_fragment_payload_max_len(max_plaintext_size())
+ linked_fragment_payload_max_len(max_plaintext_size())
} else {
(i + 1) * unlinked_fragment_payload_max_len(max_plaintext_size())
};
assert_eq!(
set.pop().unwrap().extract_payload(),
payload[lb..ub].to_vec(),
)
}
}
fn verify_two_way_linked_set_payload(mut set: FragmentSet, payload: &[u8]) {
for i in (0..set.len()).rev() {
let lb = if i == 0 {
0
} else {
(i - 1) * unlinked_fragment_payload_max_len(max_plaintext_size())
+ linked_fragment_payload_max_len(max_plaintext_size())
};
let ub = if i == (u8::max_value() as usize - 1) {
(i - 1) * unlinked_fragment_payload_max_len(max_plaintext_size())
+ 2 * linked_fragment_payload_max_len(max_plaintext_size())
} else {
i * unlinked_fragment_payload_max_len(max_plaintext_size())
+ linked_fragment_payload_max_len(max_plaintext_size())
};
assert_eq!(
set.pop().unwrap().extract_payload(),
payload[lb..ub].to_vec(),
)
}
}
fn verify_correct_link(left: &[Fragment], right: &[Fragment]) {
let first_id = left[0].id();
let post_id = left[254].next_fragments_set_id().unwrap();
let second_id = right[0].id();
let pre_id = right[0].previous_fragments_set_id().unwrap();
assert_eq!(first_id, pre_id);
assert_eq!(second_id, post_id);
}
#[cfg(test)]
mod preparing_unlinked_set {
// remember this this is only called for a sole set with <= 255 fragments
use super::*;
use rand::{thread_rng, RngCore};
#[test]
fn makes_set_with_correctly_split_payload() {
let id = 12345;
let mut rng = thread_rng();
let mut two_element_set_payload =
vec![0u8; unlinked_fragment_payload_max_len(max_plaintext_size()) + 1];
rng.fill_bytes(&mut two_element_set_payload);
let two_element_set =
prepare_unlinked_fragmented_set(&two_element_set_payload, id, max_plaintext_size());
assert_eq!(2, two_element_set.len());
verify_unlinked_set_payload(two_element_set, &two_element_set_payload);
let mut forty_two_element_set_payload =
vec![0u8; 41 * unlinked_fragment_payload_max_len(max_plaintext_size()) + 42];
rng.fill_bytes(&mut forty_two_element_set_payload);
let forty_two_element_set = prepare_unlinked_fragmented_set(
&forty_two_element_set_payload,
id,
max_plaintext_size(),
);
assert_eq!(42, forty_two_element_set.len());
verify_unlinked_set_payload(forty_two_element_set, &forty_two_element_set_payload);
let mut max_fragments_set_payload =
vec![
0u8;
max_unlinked_set_payload_length(max_plaintext_size())
- unlinked_fragment_payload_max_len(max_plaintext_size())
+ 1
]; // last fragment should have a single byte of data
rng.fill_bytes(&mut max_fragments_set_payload);
let max_fragment_set = prepare_unlinked_fragmented_set(
&max_fragments_set_payload,
id,
max_plaintext_size(),
);
assert_eq!(u8::max_value() as usize, max_fragment_set.len());
verify_unlinked_set_payload(max_fragment_set, &max_fragments_set_payload);
let mut full_set_payload =
vec![0u8; max_unlinked_set_payload_length(max_plaintext_size())];
rng.fill_bytes(&mut full_set_payload);
let full_fragment_set =
prepare_unlinked_fragmented_set(&full_set_payload, id, max_plaintext_size());
assert_eq!(u8::max_value() as usize, full_fragment_set.len());
verify_unlinked_set_payload(full_fragment_set, &full_set_payload);
}
#[test]
#[should_panic]
fn panics_for_too_long_payload() {
prepare_unlinked_fragmented_set(
&vec![0u8; max_unlinked_set_payload_length(max_plaintext_size()) + 1],
12345,
max_plaintext_size(),
);
}
}
#[cfg(test)]
mod preparing_linked_set {
use super::*;
use rand::{thread_rng, RngCore};
#[test]
fn makes_set_with_correctly_split_payload_for_pre_linked_set() {
let id = 12345;
let link_id = 1234;
let mut rng = thread_rng();
let mut two_element_set_payload =
vec![0u8; linked_fragment_payload_max_len(max_plaintext_size()) + 1];
rng.fill_bytes(&mut two_element_set_payload);
let two_element_set = prepare_linked_fragment_set(
&two_element_set_payload,
id,
Some(link_id),
None,
max_plaintext_size(),
);
assert_eq!(2, two_element_set.len());
verify_pre_linked_set_payload(two_element_set, &two_element_set_payload);
let mut forty_two_element_set_payload =
vec![
0u8;
linked_fragment_payload_max_len(max_plaintext_size())
+ 40 * unlinked_fragment_payload_max_len(max_plaintext_size())
+ 42
];
rng.fill_bytes(&mut forty_two_element_set_payload);
let forty_two_element_set = prepare_linked_fragment_set(
&forty_two_element_set_payload,
id,
Some(link_id),
None,
max_plaintext_size(),
);
assert_eq!(42, forty_two_element_set.len());
verify_pre_linked_set_payload(forty_two_element_set, &forty_two_element_set_payload);
let mut max_fragments_set_payload =
vec![
0u8;
max_unlinked_set_payload_length(max_plaintext_size())
- linked_fragment_payload_max_len(max_plaintext_size())
+ 1
]; // last fragment should have a single byte of data
rng.fill_bytes(&mut max_fragments_set_payload);
let max_fragment_set = prepare_linked_fragment_set(
&max_fragments_set_payload,
id,
Some(link_id),
None,
max_plaintext_size(),
);
assert_eq!(u8::max_value() as usize, max_fragment_set.len());
verify_pre_linked_set_payload(max_fragment_set, &max_fragments_set_payload);
let mut full_set_payload =
vec![0u8; max_one_way_linked_set_payload_length(max_plaintext_size())];
rng.fill_bytes(&mut full_set_payload);
let full_fragment_set = prepare_linked_fragment_set(
&full_set_payload,
id,
Some(link_id),
None,
max_plaintext_size(),
);
assert_eq!(u8::max_value() as usize, full_fragment_set.len());
verify_pre_linked_set_payload(full_fragment_set, &full_set_payload);
}
#[test]
#[should_panic]
fn panics_for_too_long_payload_for_pre_linked_set() {
prepare_linked_fragment_set(
&vec![0u8; max_one_way_linked_set_payload_length(max_plaintext_size()) + 1],
12345,
Some(1234),
None,
max_plaintext_size(),
);
}
#[test]
fn makes_set_with_correctly_split_payload_for_post_linked_set() {
let id = 12345;
let link_id = 1234;
let mut rng = thread_rng();
// if set is post-linked, there is only a single valid case - full length payload
let mut full_set_payload =
vec![0u8; max_one_way_linked_set_payload_length(max_plaintext_size())];
rng.fill_bytes(&mut full_set_payload);
let full_fragment_set = prepare_linked_fragment_set(
&full_set_payload,
id,
None,
Some(link_id),
max_plaintext_size(),
);
assert_eq!(u8::max_value() as usize, full_fragment_set.len());
verify_post_linked_set_payload(full_fragment_set, &full_set_payload);
}
#[test]
#[should_panic]
fn panics_for_too_long_payload_for_post_linked_set() {
prepare_linked_fragment_set(
&vec![0u8; max_one_way_linked_set_payload_length(max_plaintext_size()) + 1],
12345,
None,
Some(1234),
max_plaintext_size(),
);
}
#[test]
#[should_panic]
fn panics_for_too_short_payload_for_post_linked_set() {
prepare_linked_fragment_set(
&vec![0u8; max_one_way_linked_set_payload_length(max_plaintext_size()) - 1],
12345,
None,
Some(1234),
max_plaintext_size(),
);
}
#[test]
fn makes_set_with_correctly_split_payload_for_two_way_linked_set() {
// again, relatively simple case -
// if set is two-way-linked, there is only a single valid case - full length payload
let id = 12345;
let pre_link_id = 1234;
let post_link_id = 123456;
let mut rng = thread_rng();
let mut full_set_payload =
vec![0u8; two_way_linked_set_payload_length(max_plaintext_size())];
rng.fill_bytes(&mut full_set_payload);
let full_fragment_set = prepare_linked_fragment_set(
&full_set_payload,
id,
Some(pre_link_id),
Some(post_link_id),
max_plaintext_size(),
);
assert_eq!(u8::max_value() as usize, full_fragment_set.len());
verify_two_way_linked_set_payload(full_fragment_set, &full_set_payload);
}
#[test]
#[should_panic]
fn panics_for_too_long_payload_for_two_way_linked_set() {
prepare_linked_fragment_set(
&vec![0u8; two_way_linked_set_payload_length(max_plaintext_size()) + 1],
12345,
Some(123456),
Some(1234),
max_plaintext_size(),
);
}
#[test]
#[should_panic]
fn panics_for_too_short_payload_for_two_way_linked_set() {
prepare_linked_fragment_set(
&vec![0u8; two_way_linked_set_payload_length(max_plaintext_size()) - 1],
12345,
Some(123456),
Some(1234),
max_plaintext_size(),
);
}
}
#[cfg(test)]
mod splitting_into_sets {
use super::*;
use rand::{thread_rng, RngCore};
#[test]
fn correctly_creates_single_fragmented_set_when_expected() {
let mut rng = thread_rng();
let mut message =
vec![0u8; max_unlinked_set_payload_length(max_plaintext_size()) - 2345];
rng.fill_bytes(&mut message);
let mut sets = split_into_sets(&mut rng, &message, max_plaintext_size());
assert_eq!(1, sets.len());
verify_unlinked_set_payload(sets.pop().unwrap(), &message);
}
// a very specific test case that would have saved a lot of headache if was introduced
// earlier...
#[test]
fn correctly_creates_two_singly_linked_sets_with_second_set_containing_data_fitting_in_unfragmented_payload(
) {
let mut rng = thread_rng();
let mut message =
vec![0u8; max_one_way_linked_set_payload_length(max_plaintext_size()) + 123];
rng.fill_bytes(&mut message);
let mut sets = split_into_sets(&mut rng, &message, max_plaintext_size());
assert_eq!(2, sets.len());
verify_correct_link(&sets[0], &sets[1]);
verify_pre_linked_set_payload(
sets.pop().unwrap(),
&message[max_one_way_linked_set_payload_length(max_plaintext_size())..],
);
verify_post_linked_set_payload(
sets.pop().unwrap(),
&message[..max_one_way_linked_set_payload_length(max_plaintext_size())],
);
}
#[test]
fn correctly_creates_two_singly_linked_sets_when_expected() {
let mut rng = thread_rng();
let mut message =
vec![0u8; max_one_way_linked_set_payload_length(max_plaintext_size()) + 2345];
rng.fill_bytes(&mut message);
let mut sets = split_into_sets(&mut rng, &message, max_plaintext_size());
assert_eq!(2, sets.len());
verify_correct_link(&sets[0], &sets[1]);
verify_pre_linked_set_payload(
sets.pop().unwrap(),
&message[max_one_way_linked_set_payload_length(max_plaintext_size())..],
);
verify_post_linked_set_payload(
sets.pop().unwrap(),
&message[..max_one_way_linked_set_payload_length(max_plaintext_size())],
);
let mut message =
vec![0u8; 2 * max_one_way_linked_set_payload_length(max_plaintext_size())];
rng.fill_bytes(&mut message);
let mut sets = split_into_sets(&mut rng, &message, max_plaintext_size());
assert_eq!(2, sets.len());
assert_eq!(sets[0].len(), u8::max_value() as usize);
assert_eq!(sets[1].len(), u8::max_value() as usize);
verify_correct_link(&sets[0], &sets[1]);
verify_pre_linked_set_payload(
sets.pop().unwrap(),
&message[max_one_way_linked_set_payload_length(max_plaintext_size())..],
);
verify_post_linked_set_payload(
sets.pop().unwrap(),
&message[..max_one_way_linked_set_payload_length(max_plaintext_size())],
);
}
#[test]
fn correctly_creates_four_correctly_formed_sets_when_expected() {
let mut rng = thread_rng();
let mut message = vec![
0u8;
2 * two_way_linked_set_payload_length(max_plaintext_size())
+ max_one_way_linked_set_payload_length(max_plaintext_size())
+ 2345
];
rng.fill_bytes(&mut message);
let mut sets = split_into_sets(&mut rng, &message, max_plaintext_size());
assert_eq!(4, sets.len());
assert_eq!(sets[0].len(), u8::max_value() as usize);
assert_eq!(sets[1].len(), u8::max_value() as usize);
assert_eq!(sets[2].len(), u8::max_value() as usize);
verify_correct_link(&sets[0], &sets[1]);
verify_correct_link(&sets[1], &sets[2]);
verify_correct_link(&sets[2], &sets[3]);
verify_pre_linked_set_payload(
sets.pop().unwrap(),
&message[2 * two_way_linked_set_payload_length(max_plaintext_size())
+ max_one_way_linked_set_payload_length(max_plaintext_size())..],
);
verify_two_way_linked_set_payload(
sets.pop().unwrap(),
&message[two_way_linked_set_payload_length(max_plaintext_size())
+ max_one_way_linked_set_payload_length(max_plaintext_size())
..2 * two_way_linked_set_payload_length(max_plaintext_size())
+ max_one_way_linked_set_payload_length(max_plaintext_size())],
);
verify_two_way_linked_set_payload(
sets.pop().unwrap(),
&message[max_one_way_linked_set_payload_length(max_plaintext_size())
..two_way_linked_set_payload_length(max_plaintext_size())
+ max_one_way_linked_set_payload_length(max_plaintext_size())],
);
verify_post_linked_set_payload(
sets.pop().unwrap(),
&message[..max_one_way_linked_set_payload_length(max_plaintext_size())],
);
let mut message =
vec![
0u8;
2 * two_way_linked_set_payload_length(max_plaintext_size())
+ 2 * max_one_way_linked_set_payload_length(max_plaintext_size())
];
rng.fill_bytes(&mut message);
let mut sets = split_into_sets(&mut rng, &message, max_plaintext_size());
assert_eq!(4, sets.len());
assert_eq!(sets[0].len(), u8::max_value() as usize);
assert_eq!(sets[1].len(), u8::max_value() as usize);
assert_eq!(sets[2].len(), u8::max_value() as usize);
assert_eq!(sets[3].len(), u8::max_value() as usize);
verify_correct_link(&sets[0], &sets[1]);
verify_correct_link(&sets[1], &sets[2]);
verify_correct_link(&sets[2], &sets[3]);
verify_pre_linked_set_payload(
sets.pop().unwrap(),
&message[2 * two_way_linked_set_payload_length(max_plaintext_size())
+ max_one_way_linked_set_payload_length(max_plaintext_size())..],
);
verify_two_way_linked_set_payload(
sets.pop().unwrap(),
&message[two_way_linked_set_payload_length(max_plaintext_size())
+ max_one_way_linked_set_payload_length(max_plaintext_size())
..2 * two_way_linked_set_payload_length(max_plaintext_size())
+ max_one_way_linked_set_payload_length(max_plaintext_size())],
);
verify_two_way_linked_set_payload(
sets.pop().unwrap(),
&message[max_one_way_linked_set_payload_length(max_plaintext_size())
..two_way_linked_set_payload_length(max_plaintext_size())
+ max_one_way_linked_set_payload_length(max_plaintext_size())],
);
verify_post_linked_set_payload(
sets.pop().unwrap(),
&message[..max_one_way_linked_set_payload_length(max_plaintext_size())],
);
}
}
#[cfg(test)]
mod helpers {
use super::*;
#[test]
fn total_number_of_sets() {
assert_eq!(
1,
super::total_number_of_sets(
max_unlinked_set_payload_length(max_plaintext_size()) - 1,
max_plaintext_size()
)
);
assert_eq!(
1,
super::total_number_of_sets(
max_unlinked_set_payload_length(max_plaintext_size()),
max_plaintext_size()
)
);
assert_eq!(
2,
super::total_number_of_sets(
max_unlinked_set_payload_length(max_plaintext_size()) + 1,
max_plaintext_size()
)
);
assert_eq!(
2,
super::total_number_of_sets(
2 * max_one_way_linked_set_payload_length(max_plaintext_size()),
max_plaintext_size()
)
);
assert_eq!(
3,
super::total_number_of_sets(
2 * max_one_way_linked_set_payload_length(max_plaintext_size()) + 1,
max_plaintext_size()
)
);
assert_eq!(
3,
super::total_number_of_sets(
2 * max_one_way_linked_set_payload_length(max_plaintext_size())
+ two_way_linked_set_payload_length(max_plaintext_size())
- 1,
max_plaintext_size()
)
);
assert_eq!(
3,
super::total_number_of_sets(
2 * max_one_way_linked_set_payload_length(max_plaintext_size())
+ two_way_linked_set_payload_length(max_plaintext_size()),
max_plaintext_size()
)
);
assert_eq!(
4,
super::total_number_of_sets(
2 * max_one_way_linked_set_payload_length(max_plaintext_size())
+ two_way_linked_set_payload_length(max_plaintext_size())
+ 1,
max_plaintext_size()
)
);
assert_eq!(
4,
super::total_number_of_sets(
2 * max_one_way_linked_set_payload_length(max_plaintext_size())
+ 2 * two_way_linked_set_payload_length(max_plaintext_size())
- 1,
max_plaintext_size()
)
);
assert_eq!(
4,
super::total_number_of_sets(
2 * max_one_way_linked_set_payload_length(max_plaintext_size())
+ 2 * two_way_linked_set_payload_length(max_plaintext_size()),
max_plaintext_size()
)
);
assert_eq!(
5,
super::total_number_of_sets(
2 * max_one_way_linked_set_payload_length(max_plaintext_size())
+ 2 * two_way_linked_set_payload_length(max_plaintext_size())
+ 1,
max_plaintext_size()
)
);
}
}
}