sphinx_packet/header/
delays.rs

1// Copyright 2020 Nym Technologies SA
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::constants::DELAY_LENGTH;
16use byteorder::{BigEndian, ByteOrder};
17use rand_distr::num_traits::Zero;
18use rand_distr::{Distribution, Exp};
19use std::{borrow::Borrow, time::Duration};
20
21// TODO: once we get to proper refactoring, I think this should just be
22// a type alias to probably time::Duration
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct Delay(u64);
25
26impl Delay {
27    // Be more explicit about what kind of value we are expecting
28    pub const fn new_from_nanos(value: u64) -> Self {
29        Delay(value)
30    }
31
32    pub const fn new_from_millis(value: u64) -> Self {
33        const NANOS_PER_MILLI: u64 = 1_000_000;
34
35        Self::new_from_nanos(NANOS_PER_MILLI * value)
36    }
37
38    pub fn to_nanos(&self) -> u64 {
39        self.0
40    }
41
42    pub fn to_duration(&self) -> Duration {
43        Duration::from_nanos(self.0)
44    }
45
46    pub fn to_bytes(&self) -> [u8; DELAY_LENGTH] {
47        let mut delay_bytes = [0; DELAY_LENGTH];
48        BigEndian::write_u64(&mut delay_bytes, self.0);
49        delay_bytes
50    }
51
52    pub fn from_bytes(delay_bytes: [u8; DELAY_LENGTH]) -> Self {
53        Delay(BigEndian::read_u64(&delay_bytes))
54    }
55}
56
57impl<T> std::iter::Sum<T> for Delay
58where
59    T: Borrow<Delay>,
60{
61    fn sum<I>(iter: I) -> Self
62    where
63        I: Iterator<Item = T>,
64    {
65        iter.fold(Delay(0), |acc, item| acc + item)
66    }
67}
68
69impl<T> std::ops::Add<T> for &Delay
70where
71    T: Borrow<Delay>,
72{
73    type Output = Delay;
74    fn add(self, rhs: T) -> Self::Output {
75        *self + rhs
76    }
77}
78
79impl<T> std::ops::Add<T> for Delay
80where
81    T: Borrow<Delay>,
82{
83    type Output = Delay;
84    fn add(self, rhs: T) -> Self::Output {
85        Delay(self.0 + rhs.borrow().0)
86    }
87}
88
89impl std::ops::Mul<f64> for Delay {
90    type Output = Delay;
91    fn mul(self, rhs: f64) -> Self::Output {
92        // TODO: the question whether it's safe-ish to do it?
93        // Because for high enough delay (not sure about how "high"),
94        // the casting will not be lossless.
95        // Perhaps it's not a problem as we don't expect delays to realistically
96        // be more than minutes/hours and definitely not thousands of thousands
97        // of years.
98        Delay((self.0 as f64 * rhs) as u64)
99    }
100}
101
102// TODO: in both of those methods we are converting u64 to f64 to perform the division
103// surely this is a lossy conversion - how much does it affect us?
104
105pub fn generate_from_nanos(number: usize, average_delay: u64) -> Vec<Delay> {
106    generate_delays(number, average_delay as f64)
107}
108
109pub fn generate_from_average_duration(number: usize, average_delay: Duration) -> Vec<Delay> {
110    generate_delays(number, average_delay.as_nanos() as f64)
111}
112
113fn generate_delays(number: usize, average_delay: f64) -> Vec<Delay> {
114    if average_delay.is_zero() {
115        return vec![Delay::new_from_nanos(0); number];
116    }
117
118    let Ok(exp) = Exp::new(1.0 / average_delay) else {
119        return vec![Delay::new_from_nanos(0); number];
120    };
121
122    let mut delays = Vec::new();
123    for _ in 0..number {
124        // for now I just assume we will express it in nano-seconds to have an integer
125        delays.push(Delay::new_from_nanos(
126            exp.sample(&mut rand::thread_rng()).round() as u64,
127        ));
128    }
129    delays
130}
131
132#[cfg(test)]
133mod test_delay_generation {
134    use super::*;
135
136    #[test]
137    fn with_0_delays_returns_an_empty_vector() {
138        let delays = generate_from_average_duration(0, Duration::from_millis(10));
139        assert_eq!(0, delays.len());
140    }
141
142    #[test]
143    fn with_1_delay_it_returns_1_delay() {
144        let delays = generate_from_average_duration(1, Duration::from_secs(1));
145        assert_eq!(1, delays.len());
146    }
147
148    #[test]
149    fn with_3_delays_it_returns_3_delays() {
150        let delays = generate_from_average_duration(3, Duration::from_nanos(1));
151        assert_eq!(3, delays.len());
152    }
153
154    #[test]
155    fn it_is_possible_to_convert_it_to_and_from_bytes_without_data_loss() {
156        let expected_delay_nanos = 1_234_567_890; // 1.234... s
157        let delay = Delay::new_from_nanos(expected_delay_nanos);
158        let delay_bytes = delay.to_bytes();
159        let recovered_delay = Delay::from_bytes(delay_bytes);
160        assert_eq!(delay, recovered_delay);
161    }
162
163    #[test]
164    fn it_is_possible_to_convert_it_to_and_from_nanos_without_data_loss() {
165        let expected_delay_nanos = 1_234_567_890; // 1.234... s
166        let delay = Delay::new_from_nanos(expected_delay_nanos);
167        assert_eq!(expected_delay_nanos, delay.to_nanos());
168    }
169
170    #[test]
171    fn it_is_possible_to_convert_it_to_and_from_duration_without_data_loss() {
172        let expected_delay_nanos = 1_234_567_890; // 1.234... s
173        let delay = Delay::new_from_nanos(expected_delay_nanos);
174        let delay_duration = delay.to_duration();
175        assert_eq!(Duration::from_nanos(expected_delay_nanos), delay_duration);
176    }
177}
178
179#[cfg(test)]
180mod delay_summing {
181    use super::*;
182
183    #[test]
184    fn works_with_std_ops_only() {
185        let delay1 = Delay(42);
186        let delay2 = Delay(123);
187
188        let expected1 = Delay(165);
189        assert_eq!(expected1, delay1 + delay2);
190
191        let expected2 = Delay(265);
192        let delay3 = Delay(100);
193        assert_eq!(expected2, delay1 + delay2 + delay3)
194    }
195
196    #[test]
197    fn works_with_iterator() {
198        let delays = [Delay(42), Delay(123), Delay(100)];
199        let expected = Delay(265);
200
201        assert_eq!(expected, delays.iter().sum());
202        assert_eq!(Delay(0), Vec::new().iter().sum())
203    }
204}