1use tor_units::{
22 BoundedInt32, IntegerDays, IntegerMilliseconds, IntegerMinutes, IntegerSeconds, Percentage,
23 SendMeVersion,
24};
25
26pub const CHANNEL_PADDING_TIMEOUT_UPPER_BOUND: i32 = 60_000;
37
38pub trait FromInt32Saturating {
40 fn from_saturating(val: i32) -> Self;
46
47 fn from_checked(val: i32) -> Result<Self, tor_units::Error>
51 where
52 Self: Sized;
53}
54
55impl FromInt32Saturating for i32 {
56 fn from_saturating(val: i32) -> Self {
57 val
58 }
59
60 fn from_checked(val: i32) -> Result<Self, tor_units::Error>
61 where
62 Self: Sized,
63 {
64 Ok(val)
65 }
66}
67impl<const L: i32, const H: i32> FromInt32Saturating for BoundedInt32<L, H> {
68 fn from_saturating(val: i32) -> Self {
69 Self::saturating_new(val)
70 }
71
72 fn from_checked(val: i32) -> Result<Self, tor_units::Error>
73 where
74 Self: Sized,
75 {
76 Self::checked_new(val)
77 }
78}
79impl<T: Copy + Into<f64> + FromInt32Saturating> FromInt32Saturating for Percentage<T> {
80 fn from_saturating(val: i32) -> Self {
81 Self::new(T::from_saturating(val))
82 }
83
84 fn from_checked(val: i32) -> Result<Self, tor_units::Error>
85 where
86 Self: Sized,
87 {
88 Ok(Self::new(T::from_checked(val)?))
89 }
90}
91impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerMilliseconds<T> {
92 fn from_saturating(val: i32) -> Self {
93 Self::new(T::from_saturating(val))
94 }
95
96 fn from_checked(val: i32) -> Result<Self, tor_units::Error>
97 where
98 Self: Sized,
99 {
100 Ok(Self::new(T::from_checked(val)?))
101 }
102}
103impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerSeconds<T> {
104 fn from_saturating(val: i32) -> Self {
105 Self::new(T::from_saturating(val))
106 }
107
108 fn from_checked(val: i32) -> Result<Self, tor_units::Error>
109 where
110 Self: Sized,
111 {
112 Ok(Self::new(T::from_checked(val)?))
113 }
114}
115impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerMinutes<T> {
116 fn from_saturating(val: i32) -> Self {
117 Self::new(T::from_saturating(val))
118 }
119
120 fn from_checked(val: i32) -> Result<Self, tor_units::Error>
121 where
122 Self: Sized,
123 {
124 Ok(Self::new(T::from_checked(val)?))
125 }
126}
127impl<T: FromInt32Saturating + TryInto<u64>> FromInt32Saturating for IntegerDays<T> {
128 fn from_saturating(val: i32) -> Self {
129 Self::new(T::from_saturating(val))
130 }
131
132 fn from_checked(val: i32) -> Result<Self, tor_units::Error>
133 where
134 Self: Sized,
135 {
136 Ok(Self::new(T::from_checked(val)?))
137 }
138}
139impl FromInt32Saturating for SendMeVersion {
140 fn from_saturating(val: i32) -> Self {
141 Self::new(val.clamp(0, 255) as u8)
142 }
143
144 fn from_checked(val: i32) -> Result<Self, tor_units::Error>
145 where
146 Self: Sized,
147 {
148 let val = BoundedInt32::<0, 255>::checked_new(val)?;
149 Ok(Self::new(val.get() as u8))
150 }
151}
152
153macro_rules! declare_net_parameters {
159 {
160 $(#[$s_meta:meta])* $s_v:vis struct $s_name:ident {
161 $(
162 $(#[$p_meta:meta])* $p_v:vis
163 $p_name:ident : $p_type:ty
164 = ($p_dflt:expr) from $p_string:literal
165 ),*
166 $( , )?
167 }
168 } =>
169 {
170 $(#[$s_meta])* $s_v struct $s_name {
171 $(
172 $(#[$p_meta])* $p_v $p_name : $p_type
173 ),*
174 }
175
176 impl $s_name {
177 fn default_values() -> Result<Self, tor_units::Error> {
182 Ok(Self {
183 $( $p_name : $p_dflt.try_into()? ),*
184 })
185 }
186 fn set_saturating(&mut self, key: &str, val: i32) -> bool {
193 match key {
194 $( $p_string => self.$p_name = {
195 type T = $p_type;
196 match T::from_checked(val) {
197 Ok(v) => v,
198 Err(e) => {
199 tracing::warn!("For key {key}, clamping out of range value: {e:?}");
200 T::from_saturating(val)
201 }
202 }
203 }, )*
204 _ => return false,
205 }
206 true
207 }
208 }
209 }
210}
211
212declare_net_parameters! {
213
214#[derive(Clone, Debug)]
217#[non_exhaustive]
218pub struct NetParameters {
219 pub bw_weight_scale: BoundedInt32<1, { i32::MAX }> = (10_000)
221 from "bwweightscale",
222 pub cbt_learning_disabled: BoundedInt32<0, 1> = (0)
224 from "cbtdisabled",
225 pub cbt_num_xm_modes: BoundedInt32<1, 20> = (10)
228 from "cbtnummodes",
229 pub cbt_success_count: BoundedInt32<3, 1_000> = (20)
232 from "cbtrecentcount",
233 pub cbt_max_timeouts: BoundedInt32<3, 10_000> = (18)
236 from "cbtmaxtimeouts",
237 pub cbt_min_circs_for_estimate: BoundedInt32<1, 10_000> = (100)
240 from "cbtmincircs",
241 pub cbt_timeout_quantile: Percentage<BoundedInt32<10, 99>> = (80)
247 from "cbtquantile",
248 pub cbt_abandon_quantile: Percentage<BoundedInt32<10, 99>> = (99)
251 from "cbtclosequantile",
252 pub cbt_min_timeout: IntegerMilliseconds<BoundedInt32<10, { i32::MAX }>> = (10)
254 from "cbtmintimeout",
255 pub cbt_initial_timeout: IntegerMilliseconds<BoundedInt32<10, { i32::MAX }>> = (60_000)
258 from "cbtinitialtimeout",
259 pub cbt_testing_delay: IntegerSeconds<BoundedInt32<1, { i32::MAX }>> = (10)
263 from "cbttestfreq",
264 pub cbt_max_open_circuits_for_testing: BoundedInt32<0, 14> = (10)
268 from "cbtmaxopencircs",
269
270 pub cc_alg: BoundedInt32<0, 2> = (0)
277 from "cc_alg",
278
279 pub cc_cwnd_full_gap: BoundedInt32<0, { i16::MAX as i32 }> = (4444)
282 from "cc_cwnd_full_gap",
283 pub cc_cwnd_full_minpct: Percentage<BoundedInt32<0, 100>> = (25)
285 from "cc_cwnd_full_minpct",
286 pub cc_cwnd_full_per_cwnd: BoundedInt32<0, 1> = (1)
288 from "cc_cwnd_full_per_cwnd",
289
290 pub cc_cwnd_init: BoundedInt32<31, 10_000> = (4 * 31)
292 from "cc_cwnd_init",
293 pub cc_cwnd_inc_pct_ss: Percentage<BoundedInt32<1, 500>> = (50)
296 from "cc_cwnd_inc_pct_ss",
297 pub cc_cwnd_inc: BoundedInt32<1, 1000> = (31)
300 from "cc_cwnd_inc",
301 pub cc_cwnd_inc_rate: BoundedInt32<1, 250> = (1)
304 from "cc_cwnd_inc_rate",
305 pub cc_cwnd_min: BoundedInt32<31, 1000> = (31)
307 from "cc_cwnd_min",
308 pub cc_cwnd_max: BoundedInt32<500, { i32::MAX }> = (i32::MAX)
310 from "cc_cwnd_max",
311
312 pub cc_ewma_cwnd_pct: Percentage<BoundedInt32<1, 255>> = (50)
318 from "cc_ewma_cwnd_pct",
319 pub cc_ewma_max: BoundedInt32<2, { i32::MAX }> = (10)
321 from "cc_ewma_max",
322 pub cc_ewma_ss: BoundedInt32<2, { i32::MAX }> = (2)
324 from "cc_ewma_ss",
325 pub cc_rtt_reset_pct: Percentage<BoundedInt32<0, 100>> = (100)
328 from "cc_rtt_reset_pct",
329 pub cc_sendme_inc: BoundedInt32<1, 254> = (31)
331 from "cc_sendme_inc",
332 pub cc_ss_max: BoundedInt32<500, { i32::MAX }> = (5000)
334 from "cc_ss_max",
335
336 pub cc_vegas_alpha_exit: BoundedInt32<0, 1000> = (3 * 62)
338 from "cc_vegas_alpha_exit",
339 pub cc_vegas_beta_exit: BoundedInt32<0, 1000> = (4 * 62)
341 from "cc_vegas_beta_exit",
342 pub cc_vegas_delta_exit: BoundedInt32<0, 1000> = (5 * 62)
344 from "cc_vegas_delta_exit",
345 pub cc_vegas_gamma_exit: BoundedInt32<0, 1000> = (3 * 62)
347 from "cc_vegas_gamma_exit",
348
349 pub cc_vegas_alpha_onion: BoundedInt32<0, 1000> = (3 * 62)
351 from "cc_vegas_alpha_onion",
352 pub cc_vegas_beta_onion: BoundedInt32<0, 1000> = (6 * 62)
354 from "cc_vegas_beta_onion",
355 pub cc_vegas_delta_onion: BoundedInt32<0, 1000> = (7 * 62)
357 from "cc_vegas_delta_onion",
358 pub cc_vegas_gamma_onion: BoundedInt32<0, 1000> = (4 * 62)
360 from "cc_vegas_gamma_onion",
361
362 pub cc_vegas_sscap_exit: BoundedInt32<100, { i32::MAX }> = (600)
365 from "cc_sscap_exit",
366 pub cc_vegas_sscap_onion: BoundedInt32<100, { i32::MAX }> = (475)
369 from "cc_sscap_onion",
370
371 pub circuit_window: BoundedInt32<100, 1000> = (1_000)
373 from "circwindow",
374 pub circuit_priority_half_life: IntegerMilliseconds<BoundedInt32<1, { i32::MAX }>> = (30_000)
376 from "CircuitPriorityHalflifeMsec",
377 pub extend_by_ed25519_id: BoundedInt32<0, 1> = (0)
379 from "ExtendByEd25519ID",
380
381 pub guard_meaningful_restriction: Percentage<BoundedInt32<1,100>> = (20)
385 from "guard-meaningful-restriction-percent",
386
387 pub guard_extreme_restriction: Percentage<BoundedInt32<1,100>> = (1)
390 from "guard-extreme-restriction-percent",
391
392 pub guard_lifetime_unconfirmed: IntegerDays<BoundedInt32<1, 3650>> = (120)
395 from "guard-lifetime-days",
396
397 pub guard_lifetime_confirmed: IntegerDays<BoundedInt32<1, 3650>> = (60)
400 from "guard-confirmed-min-lifetime-days",
401
402 pub guard_internet_likely_down: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (600)
406 from "guard-internet-likely-down-interval",
407 pub guard_max_sample_size: BoundedInt32<1, {i32::MAX}> = (60)
410 from "guard-max-sample-size",
411 pub guard_max_sample_threshold: Percentage<BoundedInt32<1,100>> = (20)
414 from "guard-max-sample-threshold",
415
416 pub guard_filtered_min_sample_size: BoundedInt32<1,{i32::MAX}> = (20)
420 from "guard-min-filtered-sample-size",
421
422 pub guard_n_primary: BoundedInt32<1,{i32::MAX}> = (3)
425 from "guard-n-primary-guards",
426 pub guard_use_parallelism: BoundedInt32<1, {i32::MAX}> = (1)
429 from "guard-n-primary-guards-to-use",
430 pub guard_dir_use_parallelism: BoundedInt32<1, {i32::MAX}> = (3)
434 from "guard-n-primary-dir-guards-to-use",
435
436 pub guard_nonprimary_connect_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (15)
440 from "guard-nonprimary-guard-connect-timeout",
441 pub guard_nonprimary_idle_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (600)
444 from "guard-nonprimary-guard-idle-timeout",
445 pub guard_remove_unlisted_after: IntegerDays<BoundedInt32<1,3650>> = (20)
448 from "guard-remove-unlisted-guards-after-days",
449
450
451 pub min_circuit_path_threshold: Percentage<BoundedInt32<25, 95>> = (60)
453 from "min_paths_for_circs_pct",
454
455 pub nf_ito_low: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (1500)
459 from "nf_ito_low",
460 pub nf_ito_high: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9500)
462 from "nf_ito_high",
463 pub nf_ito_low_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9000)
465 from "nf_ito_low_reduced",
466 pub nf_ito_high_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (14000)
468 from "nf_ito_high_reduced",
469
470 pub sendme_accept_min_version: SendMeVersion = (0)
472 from "sendme_accept_min_version",
473 pub sendme_emit_min_version: SendMeVersion = (0)
475 from "sendme_emit_min_version",
476
477 pub unused_client_circ_timeout: IntegerSeconds<BoundedInt32<60, 86_400>> = (30*60)
480 from "nf_conntimeout_clients",
481 pub unused_client_circ_timeout_while_learning_cbt: IntegerSeconds<BoundedInt32<10, 60_000>> = (3*60)
484 from "cbtlearntimeout",
485
486 pub hs_introcirc_requests_min: BoundedInt32<0, {i32::MAX}> = (16384)
490 from "hs_intro_min_introduce2",
491
492 pub hs_introcirc_requests_max: BoundedInt32<0, {i32::MAX}> = (32768)
496 from "hs_intro_max_introduce2",
497
498 pub hs_intro_min_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (18 * 60 * 60)
500 from "hs_intro_min_lifetime",
501
502 pub hs_intro_max_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (24 * 60 * 60)
504 from "hs_intro_max_lifetime",
505
506 pub hs_intro_num_extra_intropoints: BoundedInt32<0, 128> = (2)
509 from "hs_intro_num_extra",
510
511 pub hsdir_dl_max_reply_cells: BoundedInt32<2, 2304> = (110)
514 from "hsdir_dl_max_reply_cells",
515
516 pub hsdir_ul_max_reply_cells: BoundedInt32<2, 1024> = (8)
519 from "hsdir_ul_max_reply_cells",
520
521 pub hsdir_timeperiod_length: IntegerMinutes<BoundedInt32<5, 14400>> = (1440)
527 from "hsdir_interval",
528
529 pub hsdir_n_replicas: BoundedInt32<1, 16> = (2)
532 from "hsdir_n_replicas",
533
534 pub hsdir_spread_fetch: BoundedInt32<1, 128> = (3)
537 from "hsdir_spread_fetch",
538
539 pub hsdir_spread_store: BoundedInt32<1,128> = (4)
542 from "hsdir_spread_store",
543
544 pub hsdir_max_desc_size: BoundedInt32<1, {i32::MAX}> = (50_000)
546 from "HSV3MaxDescriptorSize",
547
548 pub hs_service_rendezvous_failures_max: BoundedInt32<1, 10> = (2)
551 from "hs_service_max_rdv_failures",
552
553 pub hs_intro_dos_enabled: BoundedInt32<0, 1> = (0)
558 from "HiddenServiceEnableIntroDoSDefense",
559
560 pub hs_intro_dos_max_burst: BoundedInt32<0, {i32::MAX}> = (200)
566 from "HiddenServiceEnableIntroDoSBurstPerSec",
567
568 pub hs_intro_dos_rate: BoundedInt32<0, {i32::MAX}> = (25)
574 from "HiddenServiceEnableIntroDoSRatePerSec",
575
576 pub hs_pow_v1_max_effort: BoundedInt32<0, {i32::MAX}> = (10_000)
583 from "HiddenServiceProofOfWorkV1MaxEffort",
584
585 pub hs_pow_v1_service_intro_timeout: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (300)
591 from "HiddenServiceProofOfWorkV1ServiceIntroTimeoutSeconds",
592
593 pub hs_pow_v1_default_decay_adjustment: Percentage<BoundedInt32<0, 99>> = (0)
598 from "HiddenServiceProofOfWorkV1ServiceDefaultDecayAdjustment",
599
600 pub vanguards_enabled: BoundedInt32<0, 2> = (1)
611 from "vanguards-enabled",
612
613 pub vanguards_hs_service: BoundedInt32<0, 2> = (2)
626 from "vanguards-hs-service",
627
628 pub guard_hs_l2_number: BoundedInt32<1, {i32::MAX}> = (4)
633 from "guard-hs-l2-number",
634
635 pub guard_hs_l2_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (86400)
640 from "guard-hs-l2-lifetime-min",
641
642 pub guard_hs_l2_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (1036800)
647 from "guard-hs-l2-lifetime-max",
648
649 pub guard_hs_l3_number: BoundedInt32<1, {i32::MAX}> = (8)
654 from "guard-hs-l3-number",
655
656 pub guard_hs_l3_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (3600)
661 from "guard-hs-l3-lifetime-min",
662
663 pub guard_hs_l3_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (172800)
668 from "guard-hs-l3-lifetime-max",
669
670 pub kist_enabled: BoundedInt32<0, 1> = (0)
681 from "kist-enabled",
682
683 pub kist_tcp_notsent_lowat: BoundedInt32<1, {i32::MAX}> = (1)
694 from "kist-tcp-notsent-lowat",
695
696 pub use_family_lists: BoundedInt32<0,1> = (1)
699 from "use-family-lists",
700
701 pub use_family_ids: BoundedInt32<0,1> = (1)
704 from "use-family-ids",
705}
706
707}
708
709impl Default for NetParameters {
710 fn default() -> Self {
711 NetParameters::default_values().expect("Default parameters were out-of-bounds")
712 }
713}
714
715impl AsRef<NetParameters> for NetParameters {
718 fn as_ref(&self) -> &NetParameters {
719 self
720 }
721}
722
723impl NetParameters {
724 pub fn from_map(p: &tor_netdoc::doc::netstatus::NetParams<i32>) -> Self {
728 let mut params = NetParameters::default();
729 let unrecognized = params.saturating_update(p.iter());
730 for u in unrecognized {
731 tracing::debug!("Ignored unrecognized net param: {u}");
732 }
733 params
734 }
735
736 pub(crate) fn saturating_update<'a, S>(
741 &mut self,
742 iter: impl Iterator<Item = (S, &'a i32)>,
743 ) -> Vec<S>
744 where
745 S: AsRef<str>,
746 {
747 let mut unrecognized = Vec::new();
748 for (k, v) in iter {
749 if !self.set_saturating(k.as_ref(), *v) {
750 unrecognized.push(k);
751 }
752 }
753 unrecognized
754 }
755}
756
757#[cfg(test)]
758#[allow(clippy::many_single_char_names)]
759#[allow(clippy::cognitive_complexity)]
760mod test {
761 #![allow(clippy::bool_assert_comparison)]
763 #![allow(clippy::clone_on_copy)]
764 #![allow(clippy::dbg_macro)]
765 #![allow(clippy::mixed_attributes_style)]
766 #![allow(clippy::print_stderr)]
767 #![allow(clippy::print_stdout)]
768 #![allow(clippy::single_char_pattern)]
769 #![allow(clippy::unwrap_used)]
770 #![allow(clippy::unchecked_duration_subtraction)]
771 #![allow(clippy::useless_vec)]
772 #![allow(clippy::needless_pass_by_value)]
773 use super::*;
775 use std::string::String;
776
777 #[test]
778 fn empty_list() {
779 let mut x = NetParameters::default();
780 let y = Vec::<(&String, &i32)>::new();
781 let u = x.saturating_update(y.into_iter());
782 assert!(u.is_empty());
783 }
784
785 #[test]
786 fn unknown_parameter() {
787 let mut x = NetParameters::default();
788 let mut y = Vec::<(&String, &i32)>::new();
789 let k = &String::from("This_is_not_a_real_key");
790 let v = &456;
791 y.push((k, v));
792 let u = x.saturating_update(y.into_iter());
793 assert_eq!(u, vec![&String::from("This_is_not_a_real_key")]);
794 }
795
796 #[test]
800 fn single_good_parameter() {
801 let mut x = NetParameters::default();
802 let mut y = Vec::<(&String, &i32)>::new();
803 let k = &String::from("min_paths_for_circs_pct");
804 let v = &54;
805 y.push((k, v));
806 let z = x.saturating_update(y.into_iter());
807 assert!(z.is_empty());
808 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
809 }
810
811 #[test]
812 fn multiple_good_parameters() {
813 let mut x = NetParameters::default();
814 let mut y = Vec::<(&String, &i32)>::new();
815 let k = &String::from("min_paths_for_circs_pct");
816 let v = &54;
817 y.push((k, v));
818 let k = &String::from("circwindow");
819 let v = &900;
820 y.push((k, v));
821 let z = x.saturating_update(y.into_iter());
822 assert!(z.is_empty());
823 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
824 assert_eq!(x.circuit_window.get(), 900);
825 }
826
827 #[test]
828 fn good_out_of_range() {
829 let mut x = NetParameters::default();
830 let mut y = Vec::<(&String, &i32)>::new();
831 let k = &String::from("sendme_accept_min_version");
832 let v = &30;
833 y.push((k, v));
834 let k = &String::from("min_paths_for_circs_pct");
835 let v = &255;
836 y.push((k, v));
837 let z = x.saturating_update(y.into_iter());
838 assert!(z.is_empty());
839 assert_eq!(x.sendme_accept_min_version.get(), 30);
840 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
841 }
842
843 #[test]
844 fn good_invalid_rep() {
845 let mut x = NetParameters::default();
846 let mut y = Vec::<(&String, &i32)>::new();
847 let k = &String::from("sendme_accept_min_version");
848 let v = &30;
849 y.push((k, v));
850 let k = &String::from("min_paths_for_circs_pct");
851 let v = &9000;
852 y.push((k, v));
853 let z = x.saturating_update(y.into_iter());
854 assert!(z.is_empty());
855 assert_eq!(x.sendme_accept_min_version.get(), 30);
856 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
857 }
858
859 #[test]
862 fn good_unknown() {
863 let mut x = NetParameters::default();
864 let mut y = Vec::<(&String, &i32)>::new();
865 let k = &String::from("sendme_accept_min_version");
866 let v = &30;
867 y.push((k, v));
868 let k = &String::from("not_a_real_parameter");
869 let v = &9000;
870 y.push((k, v));
871 let z = x.saturating_update(y.into_iter());
872 assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
873 assert_eq!(x.sendme_accept_min_version.get(), 30);
874 }
875
876 #[test]
877 fn from_consensus() {
878 let mut p = NetParameters::default();
879 let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
880 mp.insert("bwweightscale".to_string(), 70);
881 mp.insert("min_paths_for_circs_pct".to_string(), 45);
882 mp.insert("im_a_little_teapot".to_string(), 1);
883 mp.insert("circwindow".to_string(), 99999);
884 mp.insert("ExtendByEd25519ID".to_string(), 1);
885
886 let z = p.saturating_update(mp.iter());
887 assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
888
889 assert_eq!(p.bw_weight_scale.get(), 70);
890 assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
891 let b_val: bool = p.extend_by_ed25519_id.into();
892 assert!(b_val);
893 }
894
895 #[test]
896 fn all_parameters() {
897 use std::time::Duration;
898 let mut p = NetParameters::default();
899 let mp = [
900 ("bwweightscale", 10),
901 ("cbtdisabled", 1),
902 ("cbtnummodes", 11),
903 ("cbtrecentcount", 12),
904 ("cbtmaxtimeouts", 13),
905 ("cbtmincircs", 5),
906 ("cbtquantile", 61),
907 ("cbtclosequantile", 15),
908 ("cbtlearntimeout", 1900),
909 ("cbtmintimeout", 2020),
910 ("cbtinitialtimeout", 2050),
911 ("cbttestfreq", 110),
912 ("cbtmaxopencircs", 14),
913 ("circwindow", 999),
914 ("CircuitPriorityHalflifeMsec", 222),
915 ("guard-lifetime-days", 36),
916 ("guard-confirmed-min-lifetime-days", 37),
917 ("guard-internet-likely-down-interval", 38),
918 ("guard-max-sample-size", 39),
919 ("guard-max-sample-threshold", 40),
920 ("guard-min-filtered-sample-size", 41),
921 ("guard-n-primary-guards", 42),
922 ("guard-n-primary-guards-to-use", 43),
923 ("guard-n-primary-dir-guards-to-use", 44),
924 ("guard-nonprimary-guard-connect-timeout", 45),
925 ("guard-nonprimary-guard-idle-timeout", 46),
926 ("guard-remove-unlisted-guards-after-days", 47),
927 ("guard-meaningful-restriction-percent", 12),
928 ("guard-extreme-restriction-percent", 3),
929 ("ExtendByEd25519ID", 0),
930 ("min_paths_for_circs_pct", 51),
931 ("nf_conntimeout_clients", 606),
932 ("nf_ito_low", 1_000),
933 ("nf_ito_high", 20_000),
934 ("nf_ito_low_reduced", 3_000),
935 ("nf_ito_high_reduced", 40_000),
936 ("sendme_accept_min_version", 31),
937 ("sendme_emit_min_version", 32),
938 ];
939 let ignored = p.saturating_update(mp.iter().map(|(a, b)| (a, b)));
940 assert!(ignored.is_empty());
941
942 assert_eq!(p.bw_weight_scale.get(), 10);
943 assert!(bool::from(p.cbt_learning_disabled));
944 assert_eq!(p.cbt_num_xm_modes.get(), 11);
945 assert_eq!(p.cbt_success_count.get(), 12);
946 assert_eq!(p.cbt_max_timeouts.get(), 13);
947 assert_eq!(p.cbt_min_circs_for_estimate.get(), 5);
948 assert_eq!(p.cbt_timeout_quantile.as_percent().get(), 61);
949 assert_eq!(p.cbt_abandon_quantile.as_percent().get(), 15);
950 assert_eq!(p.nf_ito_low.as_millis().get(), 1_000);
951 assert_eq!(p.nf_ito_high.as_millis().get(), 20_000);
952 assert_eq!(p.nf_ito_low_reduced.as_millis().get(), 3_000);
953 assert_eq!(p.nf_ito_high_reduced.as_millis().get(), 40_000);
954 assert_eq!(
955 Duration::try_from(p.unused_client_circ_timeout_while_learning_cbt).unwrap(),
956 Duration::from_secs(1900)
957 );
958 assert_eq!(
959 Duration::try_from(p.cbt_min_timeout).unwrap(),
960 Duration::from_millis(2020)
961 );
962 assert_eq!(
963 Duration::try_from(p.cbt_initial_timeout).unwrap(),
964 Duration::from_millis(2050)
965 );
966 assert_eq!(
967 Duration::try_from(p.cbt_testing_delay).unwrap(),
968 Duration::from_secs(110)
969 );
970 assert_eq!(p.cbt_max_open_circuits_for_testing.get(), 14);
971 assert_eq!(p.circuit_window.get(), 999);
972 assert_eq!(
973 Duration::try_from(p.circuit_priority_half_life).unwrap(),
974 Duration::from_millis(222)
975 );
976 assert!(!bool::from(p.extend_by_ed25519_id));
977 assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 51);
978 assert_eq!(
979 Duration::try_from(p.unused_client_circ_timeout).unwrap(),
980 Duration::from_secs(606)
981 );
982 assert_eq!(p.sendme_accept_min_version.get(), 31);
983 assert_eq!(p.sendme_emit_min_version.get(), 32);
984
985 assert_eq!(
986 Duration::try_from(p.guard_lifetime_unconfirmed).unwrap(),
987 Duration::from_secs(86400 * 36)
988 );
989 assert_eq!(
990 Duration::try_from(p.guard_lifetime_confirmed).unwrap(),
991 Duration::from_secs(86400 * 37)
992 );
993 assert_eq!(
994 Duration::try_from(p.guard_internet_likely_down).unwrap(),
995 Duration::from_secs(38)
996 );
997 assert_eq!(p.guard_max_sample_size.get(), 39);
998 assert_eq!(p.guard_max_sample_threshold.as_percent().get(), 40);
999 assert_eq!(p.guard_filtered_min_sample_size.get(), 41);
1000 assert_eq!(p.guard_n_primary.get(), 42);
1001 assert_eq!(p.guard_use_parallelism.get(), 43);
1002 assert_eq!(p.guard_dir_use_parallelism.get(), 44);
1003 assert_eq!(
1004 Duration::try_from(p.guard_nonprimary_connect_timeout).unwrap(),
1005 Duration::from_secs(45)
1006 );
1007 assert_eq!(
1008 Duration::try_from(p.guard_nonprimary_idle_timeout).unwrap(),
1009 Duration::from_secs(46)
1010 );
1011 assert_eq!(
1012 Duration::try_from(p.guard_remove_unlisted_after).unwrap(),
1013 Duration::from_secs(86400 * 47)
1014 );
1015 assert_eq!(p.guard_meaningful_restriction.as_percent().get(), 12);
1016 assert_eq!(p.guard_extreme_restriction.as_percent().get(), 3);
1017 }
1018}