1
//! Implements a usable view of Tor network parameters.
2
//!
3
//! The Tor consensus document contains a number of 'network
4
//! parameters', which are integer-valued items voted on by the
5
//! directory authorities.  They are used to tune the behavior of
6
//! numerous aspects of the network.
7
//! A set of Tor network parameters
8
//!
9
//! The Tor consensus document contains a number of 'network
10
//! parameters', which are integer-valued items voted on by the
11
//! directory authorities.  These parameters are used to tune the
12
//! behavior of numerous aspects of the network.
13
//!
14
//! This type differs from
15
//! [`NetParams`](tor_netdoc::doc::netstatus::NetParams) in that it
16
//! only exposes a set of parameters recognized by arti.  In return
17
//! for this restriction, it makes sure that the values it gives are
18
//! in range, and provides default values for any parameters that are
19
//! missing.
20

            
21
use tor_units::{
22
    BoundedInt32, IntegerDays, IntegerMilliseconds, IntegerMinutes, IntegerSeconds, Percentage,
23
    SendMeVersion,
24
};
25

            
26
/// Upper limit for channel padding timeouts
27
///
28
/// This is just a safety catch which might help prevent integer overflow,
29
/// and also might prevent a client getting permantently stuck in a state
30
/// where it ought to send padding but never does.
31
///
32
/// The actual value is stolen from C Tor as per
33
///   <https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/586#note_2813638>
34
/// pending an update to the specifications
35
///   <https://gitlab.torproject.org/tpo/core/torspec/-/issues/120>
36
pub const CHANNEL_PADDING_TIMEOUT_UPPER_BOUND: i32 = 60_000;
37

            
38
/// An object that can be constructed from an i32, with saturating semantics.
39
pub trait FromInt32Saturating {
40
    /// Construct an instance of this object from `val`.
41
    ///
42
    /// If `val` is too low, treat it as the lowest value that would be
43
    /// valid.  If `val` is too high, treat it as the highest value that
44
    /// would be valid.
45
    fn from_saturating(val: i32) -> Self;
46
}
47

            
48
impl FromInt32Saturating for i32 {
49
    fn from_saturating(val: i32) -> Self {
50
        val
51
    }
52
}
53
impl<const L: i32, const H: i32> FromInt32Saturating for BoundedInt32<L, H> {
54
5920
    fn from_saturating(val: i32) -> Self {
55
5920
        Self::saturating_new(val)
56
5920
    }
57
}
58
impl<T: Copy + Into<f64> + FromInt32Saturating> FromInt32Saturating for Percentage<T> {
59
618
    fn from_saturating(val: i32) -> Self {
60
618
        Self::new(T::from_saturating(val))
61
618
    }
62
}
63
impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerMilliseconds<T> {
64
680
    fn from_saturating(val: i32) -> Self {
65
680
        Self::new(T::from_saturating(val))
66
680
    }
67
}
68
impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerSeconds<T> {
69
12
    fn from_saturating(val: i32) -> Self {
70
12
        Self::new(T::from_saturating(val))
71
12
    }
72
}
73
impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerMinutes<T> {
74
4
    fn from_saturating(val: i32) -> Self {
75
4
        Self::new(T::from_saturating(val))
76
4
    }
77
}
78
impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerDays<T> {
79
6
    fn from_saturating(val: i32) -> Self {
80
6
        Self::new(T::from_saturating(val))
81
6
    }
82
}
83
impl FromInt32Saturating for SendMeVersion {
84
10
    fn from_saturating(val: i32) -> Self {
85
10
        Self::new(val.clamp(0, 255) as u8)
86
10
    }
87
}
88

            
89
/// A macro to help us declare the net parameters object.  It lets us
90
/// put the information about each parameter in just one place, even
91
/// though it will later get split between the struct declaration, the
92
/// Default implementation, and the implementation of
93
/// `saturating_update_override`.
94
macro_rules! declare_net_parameters {
95
    {
96
        $(#[$s_meta:meta])* $s_v:vis struct $s_name:ident {
97
            $(
98
                $(#[$p_meta:meta])* $p_v:vis
99
                    $p_name:ident : $p_type:ty
100
                    = ($p_dflt:expr) from $p_string:literal
101
            ),*
102
            $( , )?
103
        }
104
    } =>
105
    {
106
        $(#[$s_meta])* $s_v struct $s_name {
107
            $(
108
                $(#[$p_meta])* $p_v $p_name : $p_type
109
            ),*
110
        }
111

            
112
        impl $s_name {
113
            /// Try to construct an instance of with its default values.
114
            ///
115
            /// (This should always succeed, unless one of the default values
116
            /// is out-of-bounds for the type.)
117
6150
            fn default_values() -> Result<Self, tor_units::Error> {
118
6150
                Ok(Self {
119
6150
                    $( $p_name : $p_dflt.try_into()? ),*
120
                })
121
6150
            }
122
            /// Replace the current value for the parameter identified in the
123
            /// consensus with `key` with a new value `val`.
124
            ///
125
            /// Uses saturating semantics if the new value is out-of-range.
126
            ///
127
            /// Returns true if the key was recognized, and false otherwise.
128
6022
            fn set_saturating(&mut self, key: &str, val: i32) -> bool {
129
6022
                match key {
130
6022
                    $( $p_string => self.$p_name = {
131
3156
                        type T = $p_type;
132
3156
                        T::from_saturating(val)
133
3156
                    }, )*
134
92
                    _ => return false,
135
                }
136
5930
                true
137
6022
            }
138
        }
139
    }
140
}
141

            
142
declare_net_parameters! {
143

            
144
/// This structure holds recognized configuration parameters. All values are type-safe,
145
/// and where applicable clamped to be within range.
146
56
#[derive(Clone, Debug)]
147
#[non_exhaustive]
148
pub struct NetParameters {
149
    /// A weighting factor for bandwidth calculations
150
    pub bw_weight_scale: BoundedInt32<1, { i32::MAX }> = (10_000)
151
        from "bwweightscale",
152
    /// If true, do not attempt to learn circuit-build timeouts at all.
153
    pub cbt_learning_disabled: BoundedInt32<0, 1> = (0)
154
        from "cbtdisabled",
155
    /// Number of histograms bins to consider when estimating Xm for a
156
    /// Pareto-based circuit timeout estimator.
157
    pub cbt_num_xm_modes: BoundedInt32<1, 20> = (10)
158
        from "cbtnummodes",
159
    /// How many recent circuit success/timeout statuses do we remember
160
    /// when trying to tell if our circuit timeouts are too low?
161
    pub cbt_success_count: BoundedInt32<3, 1_000> = (20)
162
        from "cbtrecentcount",
163
    /// How many timeouts (in the last `cbt_success_count` observations)
164
    /// indicates that our circuit timeouts are too low?
165
    pub cbt_max_timeouts: BoundedInt32<3, 10_000> = (18)
166
        from "cbtmaxtimeouts",
167
    /// Smallest number of circuit build times we have to view in order to use
168
    /// our Pareto-based circuit timeout estimator.
169
    pub cbt_min_circs_for_estimate: BoundedInt32<1, 10_000> = (100)
170
        from "cbtmincircs",
171
    /// Quantile to use when determining the correct circuit timeout value
172
    /// with our Pareto estimator.
173
    ///
174
    /// (We continue building circuits after this timeout, but only
175
    /// for build-time measurement purposes.)
176
    pub cbt_timeout_quantile: Percentage<BoundedInt32<10, 99>> = (80)
177
        from "cbtquantile",
178
    /// Quantile to use when determining when to abandon circuits completely
179
    /// with our Pareto estimator.
180
    pub cbt_abandon_quantile: Percentage<BoundedInt32<10, 99>> = (99)
181
        from "cbtclosequantile",
182
    /// Lowest permissible timeout value for Pareto timeout estimator.
183
    pub cbt_min_timeout: IntegerMilliseconds<BoundedInt32<10, { i32::MAX }>> = (10)
184
        from "cbtmintimeout",
185
    /// Timeout value to use for our Pareto timeout estimator when we have
186
    /// no initial estimate.
187
    pub cbt_initial_timeout: IntegerMilliseconds<BoundedInt32<10, { i32::MAX }>> = (60_000)
188
        from "cbtinitialtimeout",
189
    /// When we don't have a good build-time estimate yet, how long
190
    /// (in seconds) do we wait between trying to launch build-time
191
    /// testing circuits through the network?
192
    pub cbt_testing_delay: IntegerSeconds<BoundedInt32<1, { i32::MAX }>> = (10)
193
        from "cbttestfreq",
194
    /// How many circuits can be open before we will no longer
195
    /// consider launching testing circuits to learn average build
196
    /// times?
197
    pub cbt_max_open_circuits_for_testing: BoundedInt32<0, 14> = (10)
198
        from "cbtmaxopencircs",
199

            
200
    /// The maximum cell window size?
201
    pub circuit_window: BoundedInt32<100, 1000> = (1_000)
202
        from "circwindow",
203
    /// The decay parameter for circuit priority
204
    pub circuit_priority_half_life: IntegerMilliseconds<BoundedInt32<1, { i32::MAX }>> = (30_000)
205
        from "CircuitPriorityHalflifeMsec",
206
    /// Whether to perform circuit extensions by Ed25519 ID
207
    pub extend_by_ed25519_id: BoundedInt32<0, 1> = (0)
208
        from "ExtendByEd25519ID",
209

            
210
    /// If we have excluded so many possible guards that the
211
    /// available fraction is below this threshold, we should use a different
212
    /// guard sample.
213
    pub guard_meaningful_restriction: Percentage<BoundedInt32<1,100>> = (20)
214
        from "guard-meaningful-restriction-percent",
215

            
216
    /// We should warn the user if they have excluded so many guards
217
    /// that the available fraction is below this threshold.
218
    pub guard_extreme_restriction: Percentage<BoundedInt32<1,100>> = (1)
219
        from "guard-extreme-restriction-percent",
220

            
221
    /// How long should we keep an unconfirmed guard (one we have not
222
    /// contacted) before removing it from the guard sample?
223
    pub guard_lifetime_unconfirmed: IntegerDays<BoundedInt32<1, 3650>> = (120)
224
        from "guard-lifetime-days",
225

            
226
    /// How long should we keep a _confirmed_ guard (one we have contacted)
227
    /// before removing it from the guard sample?
228
    pub guard_lifetime_confirmed: IntegerDays<BoundedInt32<1, 3650>> = (60)
229
        from "guard-confirmed-min-lifetime-days",
230

            
231
    /// If all circuits have failed for this interval, then treat the internet
232
    /// as "probably down", and treat any guard failures in that interval
233
    /// as unproven.
234
    pub guard_internet_likely_down: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (600)
235
        from "guard-internet-likely-down-interval",
236
    /// Largest number of guards that a client should try to maintain in
237
    /// a sample of possible guards.
238
    pub guard_max_sample_size: BoundedInt32<1, {i32::MAX}> = (60)
239
        from "guard-max-sample-size",
240
    /// Largest fraction of guard bandwidth on the network that a client
241
    /// should try to remain in a sample of possible guards.
242
    pub guard_max_sample_threshold: Percentage<BoundedInt32<1,100>> = (20)
243
        from "guard-max-sample-threshold",
244

            
245
    /// If the client ever has fewer than this many guards in their sample,
246
    /// after filtering out unusable guards, they should try to add more guards
247
    /// to the sample (if allowed).
248
    pub guard_filtered_min_sample_size: BoundedInt32<1,{i32::MAX}> = (20)
249
        from "guard-min-filtered-sample-size",
250

            
251
    /// The number of confirmed guards that the client should treat as
252
    /// "primary guards".
253
    pub guard_n_primary: BoundedInt32<1,{i32::MAX}> = (3)
254
        from "guard-n-primary-guards",
255
    /// The number of primary guards that the client should use in parallel.
256
    /// Other primary guards won't get used unless earlier ones are down.
257
    pub guard_use_parallelism: BoundedInt32<1, {i32::MAX}> = (1)
258
        from "guard-n-primary-guards-to-use",
259
    /// The number of primary guards that the client should use in
260
    /// parallel.  Other primary directory guards won't get used
261
    /// unless earlier ones are down.
262
    pub guard_dir_use_parallelism: BoundedInt32<1, {i32::MAX}> = (3)
263
        from "guard-n-primary-dir-guards-to-use",
264

            
265
    /// When trying to confirm nonprimary guards, if a guard doesn't
266
    /// answer for more than this long in seconds, treat any lower-
267
    /// priority guards as possibly usable.
268
    pub guard_nonprimary_connect_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (15)
269
        from "guard-nonprimary-guard-connect-timeout",
270
    /// When trying to confirm nonprimary guards, if a guard doesn't
271
    /// answer for more than _this_ long in seconds, treat it as down.
272
    pub guard_nonprimary_idle_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (600)
273
        from "guard-nonprimary-guard-idle-timeout",
274
    /// If a guard has been unlisted in the consensus for at least this
275
    /// long, remove it from the consensus.
276
    pub guard_remove_unlisted_after: IntegerDays<BoundedInt32<1,3650>> = (20)
277
        from "guard-remove-unlisted-guards-after-days",
278

            
279

            
280
    /// The minimum threshold for circuit patch construction
281
    pub min_circuit_path_threshold: Percentage<BoundedInt32<25, 95>> = (60)
282
        from "min_paths_for_circs_pct",
283

            
284
    /// Channel padding, low end of random padding interval, milliseconds
285
    ///
286
    /// `nf_ito` stands for "netflow inactive timeout".
287
    pub nf_ito_low: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (1500)
288
        from "nf_ito_low",
289
    /// Channel padding, high end of random padding interval, milliseconds
290
    pub nf_ito_high: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9500)
291
        from "nf_ito_high",
292
    /// Channel padding, low end of random padding interval (reduced padding) milliseconds
293
    pub nf_ito_low_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9000)
294
        from "nf_ito_low_reduced",
295
    /// Channel padding, high end of random padding interval (reduced padding) , milliseconds
296
    pub nf_ito_high_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (14000)
297
        from "nf_ito_high_reduced",
298

            
299
    /// The minimum sendme version to accept.
300
    pub sendme_accept_min_version: SendMeVersion = (0)
301
        from "sendme_accept_min_version",
302
    /// The minimum sendme version to transmit.
303
    pub sendme_emit_min_version: SendMeVersion = (0)
304
        from "sendme_emit_min_version",
305

            
306
    /// How long should never-used client circuits stay available,
307
    /// in the steady state?
308
    pub unused_client_circ_timeout: IntegerSeconds<BoundedInt32<60, 86_400>> = (30*60)
309
        from "nf_conntimeout_clients",
310
    /// When we're learning circuit timeouts, how long should never-used client
311
    /// circuits stay available?
312
    pub unused_client_circ_timeout_while_learning_cbt: IntegerSeconds<BoundedInt32<10, 60_000>> = (3*60)
313
        from "cbtlearntimeout",
314

            
315
    /// The minimum number of SENDME acks required to estimate RTT and/or bandwidth.
316
    pub cc_min_sendme_acks: BoundedInt32<2, 20> = (5)
317
        from "cc_bwe_min",
318
    /// The "N" parameter in N-EWMA smoothing of RTT and/or bandwidth estimation, specified as a
319
    /// percentage of the number of SENDME acks in a congestion window.
320
    ///
321
    /// A percentage over 100% indicates smoothing with more than one congestion window's worth
322
    /// of SENDMEs.
323
    pub cc_ewma_n_by_sendme_acks: Percentage<BoundedInt32<1, 255>> = (50)
324
        from "cc_ewma_cwnd_pct",
325
    /// The maximum value of the "N" parameter in N-EWMA smoothing of RTT and/or bandwidth
326
    /// estimation.
327
    pub cc_ewma_n_max: BoundedInt32<2, {i32::MAX}> = (10)
328
        from "cc_ewma_max",
329
    /// How many cells a SENDME acks under the congestion-control regime.
330
    pub cc_sendme_cell_ack_count: BoundedInt32<1, 255> = (31)
331
        from "cc_sendme_inc",
332
    /// How often we update our congestion window, per congestion window worth of packets.
333
    /// (For example, if this is 2, we will update the window twice every window.)
334
    pub cc_cwnd_inc_rate: BoundedInt32<1, 250> = (1)
335
        from "cc_cwnd_inc_rate",
336

            
337
    /// Lower bound on the number of INTRODUCE2 cells to allow per introduction
338
    /// circuit before the service decides to rotate to a new introduction
339
    /// circuit.
340
    pub hs_introcirc_requests_min: BoundedInt32<0, {i32::MAX}> = (16384)
341
        from "hs_intro_min_introduce2",
342

            
343
    /// Upper bound on the number of INTRODUCE2 cells to allow per introduction
344
    /// circuit before the service decides to rotate to a new introduction
345
    /// circuit.
346
    pub hs_introcirc_requests_max: BoundedInt32<0, {i32::MAX}> = (32768)
347
        from "hs_intro_max_introduce2",
348

            
349
    /// Lower bound on the lifetime of an introduction point.
350
    pub hs_intro_min_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (18 * 60 * 60)
351
        from "hs_intro_min_lifetime",
352

            
353
    /// Upper bound on the lifetime of an introduction point.
354
    pub hs_intro_max_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (24 * 60 * 60)
355
        from "hs_intro_max_lifetime",
356

            
357
    /// Number of "extra" introduction points that an onion service is allowed
358
    /// to open based on demand.
359
    pub hs_intro_num_extra_intropoints: BoundedInt32<0, 128> = (2)
360
        from "hs_intro_num_extra",
361

            
362
    /// The duration of a time period, as used in the onion service directory
363
    /// protocol.
364
    ///
365
    /// During each "time period", each onion service gets a different blinded
366
    /// ID, and the hash ring gets a new layout.
367
    pub hsdir_timeperiod_length: IntegerMinutes<BoundedInt32<30, 14400>> = (1440)
368
        from "hsdir_interval",
369

            
370
    /// The number of positions at the hash ring where an onion service
371
    /// descriptor should be stored.
372
    pub hsdir_n_replicas: BoundedInt32<1, 16> = (2)
373
        from "hsdir_n_replicas",
374

            
375
    /// The number of HSDir instances, at each position in the hash ring, that
376
    /// should be considered when downloading an onion service descriptor.
377
    pub hsdir_spread_fetch: BoundedInt32<1, 128> = (3)
378
        from "hsdir_spread_fetch",
379

            
380
    /// The number of HSDir instances, at each position in the hash ring, that
381
    /// should be considered when uploading an onion service descriptor.
382
    pub hsdir_spread_store: BoundedInt32<1,128> = (4)
383
        from "hsdir_spread_store",
384

            
385
    /// Largest allowable v3 onion service size (in bytes).
386
    pub hsdir_max_desc_size: BoundedInt32<1, {i32::MAX}> = (50_000)
387
        from "HSV3MaxDescriptorSize",
388

            
389
    /// Largest number of failures to rendezvous that an onion service should
390
    /// allow for a request.
391
    pub hs_service_rendezvous_failures_max: BoundedInt32<1, 10> = (2)
392
        from "hs_service_max_rdv_failures",
393

            
394
    /// If set to 1, introduction points use the INTRODUCE1 rate limiting
395
    /// defense when no `DosParams` are sent.
396
    ///
397
    /// See <https://spec.torproject.org/param-spec.md#HiddenServiceEnableIntroDoSDefense>
398
    pub hs_intro_dos_enabled: BoundedInt32<0, 1> = (0)
399
        from "HiddenServiceEnableIntroDoSDefense",
400

            
401
    /// Default _rate_ value for an introduction point to use for INTRODUCE1 rate
402
    /// limiting when no `DosParams` value is sent, in messages per second.
403
    ///
404
    /// See
405
    /// <https://spec.torproject.org/param-spec.md#HiddenServiceEnableIntroDoSBurstPerSec>
406
    pub hs_intro_dos_max_burst: BoundedInt32<0, {i32::MAX}> = (200)
407
        from "HiddenServiceEnableIntroDoSBurstPerSec",
408

            
409
    /// Default _burst_ value for an introduction point to use for INTRODUCE1 rate
410
    /// limiting when no `DosParams` value is sent.
411
    ///
412
    /// See
413
    /// <https://spec.torproject.org/param-spec.md#HiddenServiceEnableIntroDoSRatePerSec>
414
    pub hs_intro_dos_rate: BoundedInt32<0, {i32::MAX}> = (25)
415
        from  "HiddenServiceEnableIntroDoSRatePerSec",
416
}
417

            
418
}
419

            
420
impl Default for NetParameters {
421
6150
    fn default() -> Self {
422
6150
        NetParameters::default_values().expect("Default parameters were out-of-bounds")
423
6150
    }
424
}
425

            
426
// This impl is a bit silly, but it makes the `params` method on NetDirProvider
427
// work out.
428
impl AsRef<NetParameters> for NetParameters {
429
243
    fn as_ref(&self) -> &NetParameters {
430
243
        self
431
243
    }
432
}
433

            
434
impl NetParameters {
435
    /// Construct a new NetParameters from a given list of key=value parameters.
436
    ///
437
    /// Unrecognized parameters are ignored.
438
564
    pub fn from_map(p: &tor_netdoc::doc::netstatus::NetParams<i32>) -> Self {
439
564
        let mut params = NetParameters::default();
440
564
        let _ = params.saturating_update(p.iter());
441
564
        params
442
564
    }
443

            
444
    /// Replace a list of parameters, using the logic of
445
    /// `set_saturating`.
446
    ///
447
    /// Return a vector of the parameter names we didn't recognize.
448
7168
    pub(crate) fn saturating_update<'a, S>(
449
7168
        &mut self,
450
7168
        iter: impl Iterator<Item = (S, &'a i32)>,
451
7168
    ) -> Vec<S>
452
7168
    where
453
7168
        S: AsRef<str>,
454
7168
    {
455
7168
        let mut unrecognized = Vec::new();
456
13190
        for (k, v) in iter {
457
6022
            if !self.set_saturating(k.as_ref(), *v) {
458
92
                unrecognized.push(k);
459
6014
            }
460
        }
461
7168
        unrecognized
462
7168
    }
463
}
464

            
465
#[cfg(test)]
466
#[allow(clippy::many_single_char_names)]
467
#[allow(clippy::unwrap_used)]
468
#[allow(clippy::cognitive_complexity)]
469
mod test {
470
    // @@ begin test lint list maintained by maint/add_warning @@
471
    #![allow(clippy::bool_assert_comparison)]
472
    #![allow(clippy::clone_on_copy)]
473
    #![allow(clippy::dbg_macro)]
474
    #![allow(clippy::print_stderr)]
475
    #![allow(clippy::print_stdout)]
476
    #![allow(clippy::single_char_pattern)]
477
    #![allow(clippy::unwrap_used)]
478
    #![allow(clippy::unchecked_duration_subtraction)]
479
    #![allow(clippy::useless_vec)]
480
    #![allow(clippy::needless_pass_by_value)]
481
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
482
    use super::*;
483
    use std::string::String;
484

            
485
    #[test]
486
    fn empty_list() {
487
        let mut x = NetParameters::default();
488
        let y = Vec::<(&String, &i32)>::new();
489
        let u = x.saturating_update(y.into_iter());
490
        assert!(u.is_empty());
491
    }
492

            
493
    #[test]
494
    fn unknown_parameter() {
495
        let mut x = NetParameters::default();
496
        let mut y = Vec::<(&String, &i32)>::new();
497
        let k = &String::from("This_is_not_a_real_key");
498
        let v = &456;
499
        y.push((k, v));
500
        let u = x.saturating_update(y.into_iter());
501
        assert_eq!(u, vec![&String::from("This_is_not_a_real_key")]);
502
    }
503

            
504
    // #[test]
505
    // fn duplicate_parameter() {}
506

            
507
    #[test]
508
    fn single_good_parameter() {
509
        let mut x = NetParameters::default();
510
        let mut y = Vec::<(&String, &i32)>::new();
511
        let k = &String::from("min_paths_for_circs_pct");
512
        let v = &54;
513
        y.push((k, v));
514
        let z = x.saturating_update(y.into_iter());
515
        assert!(z.is_empty());
516
        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
517
    }
518

            
519
    #[test]
520
    fn multiple_good_parameters() {
521
        let mut x = NetParameters::default();
522
        let mut y = Vec::<(&String, &i32)>::new();
523
        let k = &String::from("min_paths_for_circs_pct");
524
        let v = &54;
525
        y.push((k, v));
526
        let k = &String::from("circwindow");
527
        let v = &900;
528
        y.push((k, v));
529
        let z = x.saturating_update(y.into_iter());
530
        assert!(z.is_empty());
531
        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
532
        assert_eq!(x.circuit_window.get(), 900);
533
    }
534

            
535
    #[test]
536
    fn good_out_of_range() {
537
        let mut x = NetParameters::default();
538
        let mut y = Vec::<(&String, &i32)>::new();
539
        let k = &String::from("sendme_accept_min_version");
540
        let v = &30;
541
        y.push((k, v));
542
        let k = &String::from("min_paths_for_circs_pct");
543
        let v = &255;
544
        y.push((k, v));
545
        let z = x.saturating_update(y.into_iter());
546
        assert!(z.is_empty());
547
        assert_eq!(x.sendme_accept_min_version.get(), 30);
548
        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
549
    }
550

            
551
    #[test]
552
    fn good_invalid_rep() {
553
        let mut x = NetParameters::default();
554
        let mut y = Vec::<(&String, &i32)>::new();
555
        let k = &String::from("sendme_accept_min_version");
556
        let v = &30;
557
        y.push((k, v));
558
        let k = &String::from("min_paths_for_circs_pct");
559
        let v = &9000;
560
        y.push((k, v));
561
        let z = x.saturating_update(y.into_iter());
562
        assert!(z.is_empty());
563
        assert_eq!(x.sendme_accept_min_version.get(), 30);
564
        assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
565
    }
566

            
567
    // #[test]
568
    // fn good_duplicate() {}
569
    #[test]
570
    fn good_unknown() {
571
        let mut x = NetParameters::default();
572
        let mut y = Vec::<(&String, &i32)>::new();
573
        let k = &String::from("sendme_accept_min_version");
574
        let v = &30;
575
        y.push((k, v));
576
        let k = &String::from("not_a_real_parameter");
577
        let v = &9000;
578
        y.push((k, v));
579
        let z = x.saturating_update(y.into_iter());
580
        assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
581
        assert_eq!(x.sendme_accept_min_version.get(), 30);
582
    }
583

            
584
    #[test]
585
    fn from_consensus() {
586
        let mut p = NetParameters::default();
587
        let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
588
        mp.insert("bwweightscale".to_string(), 70);
589
        mp.insert("min_paths_for_circs_pct".to_string(), 45);
590
        mp.insert("im_a_little_teapot".to_string(), 1);
591
        mp.insert("circwindow".to_string(), 99999);
592
        mp.insert("ExtendByEd25519ID".to_string(), 1);
593

            
594
        let z = p.saturating_update(mp.iter());
595
        assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
596

            
597
        assert_eq!(p.bw_weight_scale.get(), 70);
598
        assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
599
        let b_val: bool = p.extend_by_ed25519_id.into();
600
        assert!(b_val);
601
    }
602

            
603
    #[test]
604
    // TODO remove when this upstream bug is fixed
605
    ///  https://github.com/rust-lang/rust-clippy/issues/11764
606
    #[allow(clippy::map_identity)]
607
    fn all_parameters() {
608
        use std::time::Duration;
609
        let mut p = NetParameters::default();
610
        let mp = [
611
            ("bwweightscale", 10),
612
            ("cbtdisabled", 1),
613
            ("cbtnummodes", 11),
614
            ("cbtrecentcount", 12),
615
            ("cbtmaxtimeouts", 13),
616
            ("cbtmincircs", 5),
617
            ("cbtquantile", 61),
618
            ("cbtclosequantile", 15),
619
            ("cbtlearntimeout", 1900),
620
            ("cbtmintimeout", 2020),
621
            ("cbtinitialtimeout", 2050),
622
            ("cbttestfreq", 110),
623
            ("cbtmaxopencircs", 14),
624
            ("circwindow", 999),
625
            ("CircuitPriorityHalflifeMsec", 222),
626
            ("guard-lifetime-days", 36),
627
            ("guard-confirmed-min-lifetime-days", 37),
628
            ("guard-internet-likely-down-interval", 38),
629
            ("guard-max-sample-size", 39),
630
            ("guard-max-sample-threshold", 40),
631
            ("guard-min-filtered-sample-size", 41),
632
            ("guard-n-primary-guards", 42),
633
            ("guard-n-primary-guards-to-use", 43),
634
            ("guard-n-primary-dir-guards-to-use", 44),
635
            ("guard-nonprimary-guard-connect-timeout", 45),
636
            ("guard-nonprimary-guard-idle-timeout", 46),
637
            ("guard-remove-unlisted-guards-after-days", 47),
638
            ("guard-meaningful-restriction-percent", 12),
639
            ("guard-extreme-restriction-percent", 3),
640
            ("ExtendByEd25519ID", 0),
641
            ("min_paths_for_circs_pct", 51),
642
            ("nf_conntimeout_clients", 606),
643
            ("nf_ito_low", 1_000),
644
            ("nf_ito_high", 20_000),
645
            ("nf_ito_low_reduced", 3_000),
646
            ("nf_ito_high_reduced", 40_000),
647
            ("sendme_accept_min_version", 31),
648
            ("sendme_emit_min_version", 32),
649
        ];
650
        let ignored = p.saturating_update(mp.iter().map(|(a, b)| (a, b)));
651
        assert!(ignored.is_empty());
652

            
653
        assert_eq!(p.bw_weight_scale.get(), 10);
654
        assert!(bool::from(p.cbt_learning_disabled));
655
        assert_eq!(p.cbt_num_xm_modes.get(), 11);
656
        assert_eq!(p.cbt_success_count.get(), 12);
657
        assert_eq!(p.cbt_max_timeouts.get(), 13);
658
        assert_eq!(p.cbt_min_circs_for_estimate.get(), 5);
659
        assert_eq!(p.cbt_timeout_quantile.as_percent().get(), 61);
660
        assert_eq!(p.cbt_abandon_quantile.as_percent().get(), 15);
661
        assert_eq!(p.nf_ito_low.as_millis().get(), 1_000);
662
        assert_eq!(p.nf_ito_high.as_millis().get(), 20_000);
663
        assert_eq!(p.nf_ito_low_reduced.as_millis().get(), 3_000);
664
        assert_eq!(p.nf_ito_high_reduced.as_millis().get(), 40_000);
665
        assert_eq!(
666
            Duration::try_from(p.unused_client_circ_timeout_while_learning_cbt).unwrap(),
667
            Duration::from_secs(1900)
668
        );
669
        assert_eq!(
670
            Duration::try_from(p.cbt_min_timeout).unwrap(),
671
            Duration::from_millis(2020)
672
        );
673
        assert_eq!(
674
            Duration::try_from(p.cbt_initial_timeout).unwrap(),
675
            Duration::from_millis(2050)
676
        );
677
        assert_eq!(
678
            Duration::try_from(p.cbt_testing_delay).unwrap(),
679
            Duration::from_secs(110)
680
        );
681
        assert_eq!(p.cbt_max_open_circuits_for_testing.get(), 14);
682
        assert_eq!(p.circuit_window.get(), 999);
683
        assert_eq!(
684
            Duration::try_from(p.circuit_priority_half_life).unwrap(),
685
            Duration::from_millis(222)
686
        );
687
        assert!(!bool::from(p.extend_by_ed25519_id));
688
        assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 51);
689
        assert_eq!(
690
            Duration::try_from(p.unused_client_circ_timeout).unwrap(),
691
            Duration::from_secs(606)
692
        );
693
        assert_eq!(p.sendme_accept_min_version.get(), 31);
694
        assert_eq!(p.sendme_emit_min_version.get(), 32);
695

            
696
        assert_eq!(
697
            Duration::try_from(p.guard_lifetime_unconfirmed).unwrap(),
698
            Duration::from_secs(86400 * 36)
699
        );
700
        assert_eq!(
701
            Duration::try_from(p.guard_lifetime_confirmed).unwrap(),
702
            Duration::from_secs(86400 * 37)
703
        );
704
        assert_eq!(
705
            Duration::try_from(p.guard_internet_likely_down).unwrap(),
706
            Duration::from_secs(38)
707
        );
708
        assert_eq!(p.guard_max_sample_size.get(), 39);
709
        assert_eq!(p.guard_max_sample_threshold.as_percent().get(), 40);
710
        assert_eq!(p.guard_filtered_min_sample_size.get(), 41);
711
        assert_eq!(p.guard_n_primary.get(), 42);
712
        assert_eq!(p.guard_use_parallelism.get(), 43);
713
        assert_eq!(p.guard_dir_use_parallelism.get(), 44);
714
        assert_eq!(
715
            Duration::try_from(p.guard_nonprimary_connect_timeout).unwrap(),
716
            Duration::from_secs(45)
717
        );
718
        assert_eq!(
719
            Duration::try_from(p.guard_nonprimary_idle_timeout).unwrap(),
720
            Duration::from_secs(46)
721
        );
722
        assert_eq!(
723
            Duration::try_from(p.guard_remove_unlisted_after).unwrap(),
724
            Duration::from_secs(86400 * 47)
725
        );
726
        assert_eq!(p.guard_meaningful_restriction.as_percent().get(), 12);
727
        assert_eq!(p.guard_extreme_restriction.as_percent().get(), 3);
728
    }
729
}