/ crates / tor-netdir / src / params.rs
params.rs
  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      fn from_saturating(val: i32) -> Self {
 55          Self::saturating_new(val)
 56      }
 57  }
 58  impl<T: Copy + Into<f64> + FromInt32Saturating> FromInt32Saturating for Percentage<T> {
 59      fn from_saturating(val: i32) -> Self {
 60          Self::new(T::from_saturating(val))
 61      }
 62  }
 63  impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerMilliseconds<T> {
 64      fn from_saturating(val: i32) -> Self {
 65          Self::new(T::from_saturating(val))
 66      }
 67  }
 68  impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerSeconds<T> {
 69      fn from_saturating(val: i32) -> Self {
 70          Self::new(T::from_saturating(val))
 71      }
 72  }
 73  impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerMinutes<T> {
 74      fn from_saturating(val: i32) -> Self {
 75          Self::new(T::from_saturating(val))
 76      }
 77  }
 78  impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerDays<T> {
 79      fn from_saturating(val: i32) -> Self {
 80          Self::new(T::from_saturating(val))
 81      }
 82  }
 83  impl FromInt32Saturating for SendMeVersion {
 84      fn from_saturating(val: i32) -> Self {
 85          Self::new(val.clamp(0, 255) as u8)
 86      }
 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              fn default_values() -> Result<Self, tor_units::Error> {
118                  Ok(Self {
119                      $( $p_name : $p_dflt.try_into()? ),*
120                  })
121              }
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              fn set_saturating(&mut self, key: &str, val: i32) -> bool {
129                  match key {
130                      $( $p_string => self.$p_name = {
131                          type T = $p_type;
132                          T::from_saturating(val)
133                      }, )*
134                      _ => return false,
135                  }
136                  true
137              }
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  #[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.html#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.html#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.html#HiddenServiceEnableIntroDoSRatePerSec>
414      pub hs_intro_dos_rate: BoundedInt32<0, {i32::MAX}> = (25)
415          from  "HiddenServiceEnableIntroDoSRatePerSec",
416  
417      /// The type of vanguards to use by default when building onion service circuits:
418      ///
419      /// ```text
420      ///    0: No vanguards.
421      ///    1: Lite vanguards.
422      ///    2: Full vanguards.
423      /// ```
424      ///
425      /// See
426      /// <https://spec.torproject.org/param-spec.html#vanguards>
427      pub vanguards_enabled: BoundedInt32<0, 2> = (1)
428          from "vanguards-enabled",
429  
430      /// If higher than `vanguards-enabled`,
431      /// and we are running an onion service,
432      /// we use this level for all our onion service circuits:
433      ///
434      /// ```text
435      ///    0: No vanguards.
436      ///    1: Lite vanguards.
437      ///    2: Full vanguards.
438      /// ```
439      ///
440      /// See
441      /// <https://spec.torproject.org/param-spec.html#vanguards>
442      pub vanguards_hs_service: BoundedInt32<0, 2> = (2)
443          from "vanguards-hs-service",
444  
445      /// The number of vanguards in the L2 vanguard set.
446      ///
447      /// See
448      /// <https://spec.torproject.org/param-spec.html#vanguards>
449      pub guard_hs_l2_number: BoundedInt32<1, {i32::MAX}> = (4)
450          from  "guard-hs-l2-number",
451  
452      /// The minimum lifetime of L2 vanguards.
453      ///
454      /// See
455      /// <https://spec.torproject.org/param-spec.html#vanguards>
456      pub guard_hs_l2_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (86400)
457          from  "guard-hs-l2-lifetime-min",
458  
459      /// The maximum lifetime of L2 vanguards.
460      ///
461      /// See
462      /// <https://spec.torproject.org/param-spec.html#vanguards>
463      pub guard_hs_l2_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (1036800)
464          from  "guard-hs-l2-lifetime-max",
465  
466      /// The number of vanguards in the L3 vanguard set.
467      ///
468      /// See
469      /// <https://spec.torproject.org/param-spec.html#vanguards>
470      pub guard_hs_l3_number: BoundedInt32<1, {i32::MAX}> = (8)
471          from  "guard-hs-l3-number",
472  
473      /// The minimum lifetime of L3 vanguards.
474      ///
475      /// See
476      /// <https://spec.torproject.org/param-spec.html#vanguards>
477      pub guard_hs_l3_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (3600)
478          from  "guard-hs-l3-lifetime-min",
479  
480      /// The maximum lifetime of L3 vanguards.
481      ///
482      /// See
483      /// <https://spec.torproject.org/param-spec.html#vanguards>
484      pub guard_hs_l3_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (172800)
485          from  "guard-hs-l3-lifetime-max",
486  }
487  
488  }
489  
490  impl Default for NetParameters {
491      fn default() -> Self {
492          NetParameters::default_values().expect("Default parameters were out-of-bounds")
493      }
494  }
495  
496  // This impl is a bit silly, but it makes the `params` method on NetDirProvider
497  // work out.
498  impl AsRef<NetParameters> for NetParameters {
499      fn as_ref(&self) -> &NetParameters {
500          self
501      }
502  }
503  
504  impl NetParameters {
505      /// Construct a new NetParameters from a given list of key=value parameters.
506      ///
507      /// Unrecognized parameters are ignored.
508      pub fn from_map(p: &tor_netdoc::doc::netstatus::NetParams<i32>) -> Self {
509          let mut params = NetParameters::default();
510          let _ = params.saturating_update(p.iter());
511          params
512      }
513  
514      /// Replace a list of parameters, using the logic of
515      /// `set_saturating`.
516      ///
517      /// Return a vector of the parameter names we didn't recognize.
518      pub(crate) fn saturating_update<'a, S>(
519          &mut self,
520          iter: impl Iterator<Item = (S, &'a i32)>,
521      ) -> Vec<S>
522      where
523          S: AsRef<str>,
524      {
525          let mut unrecognized = Vec::new();
526          for (k, v) in iter {
527              if !self.set_saturating(k.as_ref(), *v) {
528                  unrecognized.push(k);
529              }
530          }
531          unrecognized
532      }
533  }
534  
535  #[cfg(test)]
536  #[allow(clippy::many_single_char_names)]
537  #[allow(clippy::unwrap_used)]
538  #[allow(clippy::cognitive_complexity)]
539  mod test {
540      // @@ begin test lint list maintained by maint/add_warning @@
541      #![allow(clippy::bool_assert_comparison)]
542      #![allow(clippy::clone_on_copy)]
543      #![allow(clippy::dbg_macro)]
544      #![allow(clippy::mixed_attributes_style)]
545      #![allow(clippy::print_stderr)]
546      #![allow(clippy::print_stdout)]
547      #![allow(clippy::single_char_pattern)]
548      #![allow(clippy::unwrap_used)]
549      #![allow(clippy::unchecked_duration_subtraction)]
550      #![allow(clippy::useless_vec)]
551      #![allow(clippy::needless_pass_by_value)]
552      //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
553      use super::*;
554      use std::string::String;
555  
556      #[test]
557      fn empty_list() {
558          let mut x = NetParameters::default();
559          let y = Vec::<(&String, &i32)>::new();
560          let u = x.saturating_update(y.into_iter());
561          assert!(u.is_empty());
562      }
563  
564      #[test]
565      fn unknown_parameter() {
566          let mut x = NetParameters::default();
567          let mut y = Vec::<(&String, &i32)>::new();
568          let k = &String::from("This_is_not_a_real_key");
569          let v = &456;
570          y.push((k, v));
571          let u = x.saturating_update(y.into_iter());
572          assert_eq!(u, vec![&String::from("This_is_not_a_real_key")]);
573      }
574  
575      // #[test]
576      // fn duplicate_parameter() {}
577  
578      #[test]
579      fn single_good_parameter() {
580          let mut x = NetParameters::default();
581          let mut y = Vec::<(&String, &i32)>::new();
582          let k = &String::from("min_paths_for_circs_pct");
583          let v = &54;
584          y.push((k, v));
585          let z = x.saturating_update(y.into_iter());
586          assert!(z.is_empty());
587          assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
588      }
589  
590      #[test]
591      fn multiple_good_parameters() {
592          let mut x = NetParameters::default();
593          let mut y = Vec::<(&String, &i32)>::new();
594          let k = &String::from("min_paths_for_circs_pct");
595          let v = &54;
596          y.push((k, v));
597          let k = &String::from("circwindow");
598          let v = &900;
599          y.push((k, v));
600          let z = x.saturating_update(y.into_iter());
601          assert!(z.is_empty());
602          assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
603          assert_eq!(x.circuit_window.get(), 900);
604      }
605  
606      #[test]
607      fn good_out_of_range() {
608          let mut x = NetParameters::default();
609          let mut y = Vec::<(&String, &i32)>::new();
610          let k = &String::from("sendme_accept_min_version");
611          let v = &30;
612          y.push((k, v));
613          let k = &String::from("min_paths_for_circs_pct");
614          let v = &255;
615          y.push((k, v));
616          let z = x.saturating_update(y.into_iter());
617          assert!(z.is_empty());
618          assert_eq!(x.sendme_accept_min_version.get(), 30);
619          assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
620      }
621  
622      #[test]
623      fn good_invalid_rep() {
624          let mut x = NetParameters::default();
625          let mut y = Vec::<(&String, &i32)>::new();
626          let k = &String::from("sendme_accept_min_version");
627          let v = &30;
628          y.push((k, v));
629          let k = &String::from("min_paths_for_circs_pct");
630          let v = &9000;
631          y.push((k, v));
632          let z = x.saturating_update(y.into_iter());
633          assert!(z.is_empty());
634          assert_eq!(x.sendme_accept_min_version.get(), 30);
635          assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
636      }
637  
638      // #[test]
639      // fn good_duplicate() {}
640      #[test]
641      fn good_unknown() {
642          let mut x = NetParameters::default();
643          let mut y = Vec::<(&String, &i32)>::new();
644          let k = &String::from("sendme_accept_min_version");
645          let v = &30;
646          y.push((k, v));
647          let k = &String::from("not_a_real_parameter");
648          let v = &9000;
649          y.push((k, v));
650          let z = x.saturating_update(y.into_iter());
651          assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
652          assert_eq!(x.sendme_accept_min_version.get(), 30);
653      }
654  
655      #[test]
656      fn from_consensus() {
657          let mut p = NetParameters::default();
658          let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
659          mp.insert("bwweightscale".to_string(), 70);
660          mp.insert("min_paths_for_circs_pct".to_string(), 45);
661          mp.insert("im_a_little_teapot".to_string(), 1);
662          mp.insert("circwindow".to_string(), 99999);
663          mp.insert("ExtendByEd25519ID".to_string(), 1);
664  
665          let z = p.saturating_update(mp.iter());
666          assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
667  
668          assert_eq!(p.bw_weight_scale.get(), 70);
669          assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
670          let b_val: bool = p.extend_by_ed25519_id.into();
671          assert!(b_val);
672      }
673  
674      #[test]
675      // TODO remove when this upstream bug is fixed
676      ///  https://github.com/rust-lang/rust-clippy/issues/11764
677      #[allow(clippy::map_identity)]
678      fn all_parameters() {
679          use std::time::Duration;
680          let mut p = NetParameters::default();
681          let mp = [
682              ("bwweightscale", 10),
683              ("cbtdisabled", 1),
684              ("cbtnummodes", 11),
685              ("cbtrecentcount", 12),
686              ("cbtmaxtimeouts", 13),
687              ("cbtmincircs", 5),
688              ("cbtquantile", 61),
689              ("cbtclosequantile", 15),
690              ("cbtlearntimeout", 1900),
691              ("cbtmintimeout", 2020),
692              ("cbtinitialtimeout", 2050),
693              ("cbttestfreq", 110),
694              ("cbtmaxopencircs", 14),
695              ("circwindow", 999),
696              ("CircuitPriorityHalflifeMsec", 222),
697              ("guard-lifetime-days", 36),
698              ("guard-confirmed-min-lifetime-days", 37),
699              ("guard-internet-likely-down-interval", 38),
700              ("guard-max-sample-size", 39),
701              ("guard-max-sample-threshold", 40),
702              ("guard-min-filtered-sample-size", 41),
703              ("guard-n-primary-guards", 42),
704              ("guard-n-primary-guards-to-use", 43),
705              ("guard-n-primary-dir-guards-to-use", 44),
706              ("guard-nonprimary-guard-connect-timeout", 45),
707              ("guard-nonprimary-guard-idle-timeout", 46),
708              ("guard-remove-unlisted-guards-after-days", 47),
709              ("guard-meaningful-restriction-percent", 12),
710              ("guard-extreme-restriction-percent", 3),
711              ("ExtendByEd25519ID", 0),
712              ("min_paths_for_circs_pct", 51),
713              ("nf_conntimeout_clients", 606),
714              ("nf_ito_low", 1_000),
715              ("nf_ito_high", 20_000),
716              ("nf_ito_low_reduced", 3_000),
717              ("nf_ito_high_reduced", 40_000),
718              ("sendme_accept_min_version", 31),
719              ("sendme_emit_min_version", 32),
720          ];
721          let ignored = p.saturating_update(mp.iter().map(|(a, b)| (a, b)));
722          assert!(ignored.is_empty());
723  
724          assert_eq!(p.bw_weight_scale.get(), 10);
725          assert!(bool::from(p.cbt_learning_disabled));
726          assert_eq!(p.cbt_num_xm_modes.get(), 11);
727          assert_eq!(p.cbt_success_count.get(), 12);
728          assert_eq!(p.cbt_max_timeouts.get(), 13);
729          assert_eq!(p.cbt_min_circs_for_estimate.get(), 5);
730          assert_eq!(p.cbt_timeout_quantile.as_percent().get(), 61);
731          assert_eq!(p.cbt_abandon_quantile.as_percent().get(), 15);
732          assert_eq!(p.nf_ito_low.as_millis().get(), 1_000);
733          assert_eq!(p.nf_ito_high.as_millis().get(), 20_000);
734          assert_eq!(p.nf_ito_low_reduced.as_millis().get(), 3_000);
735          assert_eq!(p.nf_ito_high_reduced.as_millis().get(), 40_000);
736          assert_eq!(
737              Duration::try_from(p.unused_client_circ_timeout_while_learning_cbt).unwrap(),
738              Duration::from_secs(1900)
739          );
740          assert_eq!(
741              Duration::try_from(p.cbt_min_timeout).unwrap(),
742              Duration::from_millis(2020)
743          );
744          assert_eq!(
745              Duration::try_from(p.cbt_initial_timeout).unwrap(),
746              Duration::from_millis(2050)
747          );
748          assert_eq!(
749              Duration::try_from(p.cbt_testing_delay).unwrap(),
750              Duration::from_secs(110)
751          );
752          assert_eq!(p.cbt_max_open_circuits_for_testing.get(), 14);
753          assert_eq!(p.circuit_window.get(), 999);
754          assert_eq!(
755              Duration::try_from(p.circuit_priority_half_life).unwrap(),
756              Duration::from_millis(222)
757          );
758          assert!(!bool::from(p.extend_by_ed25519_id));
759          assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 51);
760          assert_eq!(
761              Duration::try_from(p.unused_client_circ_timeout).unwrap(),
762              Duration::from_secs(606)
763          );
764          assert_eq!(p.sendme_accept_min_version.get(), 31);
765          assert_eq!(p.sendme_emit_min_version.get(), 32);
766  
767          assert_eq!(
768              Duration::try_from(p.guard_lifetime_unconfirmed).unwrap(),
769              Duration::from_secs(86400 * 36)
770          );
771          assert_eq!(
772              Duration::try_from(p.guard_lifetime_confirmed).unwrap(),
773              Duration::from_secs(86400 * 37)
774          );
775          assert_eq!(
776              Duration::try_from(p.guard_internet_likely_down).unwrap(),
777              Duration::from_secs(38)
778          );
779          assert_eq!(p.guard_max_sample_size.get(), 39);
780          assert_eq!(p.guard_max_sample_threshold.as_percent().get(), 40);
781          assert_eq!(p.guard_filtered_min_sample_size.get(), 41);
782          assert_eq!(p.guard_n_primary.get(), 42);
783          assert_eq!(p.guard_use_parallelism.get(), 43);
784          assert_eq!(p.guard_dir_use_parallelism.get(), 44);
785          assert_eq!(
786              Duration::try_from(p.guard_nonprimary_connect_timeout).unwrap(),
787              Duration::from_secs(45)
788          );
789          assert_eq!(
790              Duration::try_from(p.guard_nonprimary_idle_timeout).unwrap(),
791              Duration::from_secs(46)
792          );
793          assert_eq!(
794              Duration::try_from(p.guard_remove_unlisted_after).unwrap(),
795              Duration::from_secs(86400 * 47)
796          );
797          assert_eq!(p.guard_meaningful_restriction.as_percent().get(), 12);
798          assert_eq!(p.guard_extreme_restriction.as_percent().get(), 3);
799      }
800  }