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> = (2)
273 from "cc_alg",
274
275 pub cc_cwnd_full_gap: BoundedInt32<0, { i16::MAX as i32 }> = (4444)
278 from "cc_cwnd_full_gap",
279 pub cc_cwnd_full_minpct: Percentage<BoundedInt32<0, 100>> = (25)
281 from "cc_cwnd_full_minpct",
282 pub cc_cwnd_full_per_cwnd: BoundedInt32<0, 1> = (1)
284 from "cc_cwnd_full_per_cwnd",
285
286 pub cc_cwnd_init: BoundedInt32<31, 10_000> = (4 * 31)
288 from "cc_cwnd_init",
289 pub cc_cwnd_inc_pct_ss: Percentage<BoundedInt32<1, 500>> = (50)
292 from "cc_cwnd_inc_pct_ss",
293 pub cc_cwnd_inc: BoundedInt32<1, 1000> = (31)
296 from "cc_cwnd_inc",
297 pub cc_cwnd_inc_rate: BoundedInt32<1, 250> = (1)
300 from "cc_cwnd_inc_rate",
301 pub cc_cwnd_min: BoundedInt32<31, 1000> = (31)
303 from "cc_cwnd_min",
304 pub cc_cwnd_max: BoundedInt32<500, { i32::MAX }> = (i32::MAX)
306 from "cc_cwnd_max",
307
308 pub cc_ewma_cwnd_pct: Percentage<BoundedInt32<1, 255>> = (50)
314 from "cc_ewma_cwnd_pct",
315 pub cc_ewma_max: BoundedInt32<2, { i32::MAX }> = (10)
317 from "cc_ewma_max",
318 pub cc_ewma_ss: BoundedInt32<2, { i32::MAX }> = (2)
320 from "cc_ewma_ss",
321 pub cc_rtt_reset_pct: Percentage<BoundedInt32<0, 100>> = (100)
324 from "cc_rtt_reset_pct",
325 pub cc_sendme_inc: BoundedInt32<1, 254> = (31)
327 from "cc_sendme_inc",
328 pub cc_ss_max: BoundedInt32<500, { i32::MAX }> = (5000)
330 from "cc_ss_max",
331
332 pub cc_vegas_alpha_exit: BoundedInt32<0, 1000> = (3 * 62)
334 from "cc_vegas_alpha_exit",
335 pub cc_vegas_beta_exit: BoundedInt32<0, 1000> = (4 * 62)
337 from "cc_vegas_beta_exit",
338 pub cc_vegas_delta_exit: BoundedInt32<0, 1000> = (5 * 62)
340 from "cc_vegas_delta_exit",
341 pub cc_vegas_gamma_exit: BoundedInt32<0, 1000> = (3 * 62)
343 from "cc_vegas_gamma_exit",
344
345 pub cc_vegas_alpha_onion: BoundedInt32<0, 1000> = (3 * 62)
347 from "cc_vegas_alpha_onion",
348 pub cc_vegas_beta_onion: BoundedInt32<0, 1000> = (6 * 62)
350 from "cc_vegas_beta_onion",
351 pub cc_vegas_delta_onion: BoundedInt32<0, 1000> = (7 * 62)
353 from "cc_vegas_delta_onion",
354 pub cc_vegas_gamma_onion: BoundedInt32<0, 1000> = (4 * 62)
356 from "cc_vegas_gamma_onion",
357
358 pub cc_vegas_sscap_exit: BoundedInt32<100, { i32::MAX }> = (600)
361 from "cc_sscap_exit",
362 pub cc_vegas_sscap_onion: BoundedInt32<100, { i32::MAX }> = (475)
365 from "cc_sscap_onion",
366
367 pub cc_xoff_client: BoundedInt32<1, 10_000> = (500)
376 from "cc_xoff_client",
377 pub cc_xoff_exit: BoundedInt32<1, 10_000> = (500)
382 from "cc_xoff_exit",
383 pub cc_xon_rate: BoundedInt32<1, 5000> = (500)
388 from "cc_xon_rate",
389 pub cc_xon_change_pct: BoundedInt32<1, 99> = (25)
393 from "cc_xon_change_pct",
394 pub cc_xon_ewma_cnt: BoundedInt32<2, 100> = (2)
398 from "cc_xon_ewma_cnt",
399
400 pub circuit_window: BoundedInt32<100, 1000> = (1_000)
402 from "circwindow",
403 pub circuit_priority_half_life: IntegerMilliseconds<BoundedInt32<1, { i32::MAX }>> = (30_000)
405 from "CircuitPriorityHalflifeMsec",
406 pub extend_by_ed25519_id: BoundedInt32<0, 1> = (0)
408 from "ExtendByEd25519ID",
409
410 pub guard_meaningful_restriction: Percentage<BoundedInt32<1,100>> = (20)
414 from "guard-meaningful-restriction-percent",
415
416 pub guard_extreme_restriction: Percentage<BoundedInt32<1,100>> = (1)
419 from "guard-extreme-restriction-percent",
420
421 pub guard_lifetime_unconfirmed: IntegerDays<BoundedInt32<1, 3650>> = (120)
424 from "guard-lifetime-days",
425
426 pub guard_lifetime_confirmed: IntegerDays<BoundedInt32<1, 3650>> = (60)
429 from "guard-confirmed-min-lifetime-days",
430
431 pub guard_internet_likely_down: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (600)
435 from "guard-internet-likely-down-interval",
436 pub guard_max_sample_size: BoundedInt32<1, {i32::MAX}> = (60)
439 from "guard-max-sample-size",
440 pub guard_max_sample_threshold: Percentage<BoundedInt32<1,100>> = (20)
443 from "guard-max-sample-threshold",
444
445 pub guard_filtered_min_sample_size: BoundedInt32<1,{i32::MAX}> = (20)
449 from "guard-min-filtered-sample-size",
450
451 pub guard_n_primary: BoundedInt32<1,{i32::MAX}> = (3)
454 from "guard-n-primary-guards",
455 pub guard_use_parallelism: BoundedInt32<1, {i32::MAX}> = (1)
458 from "guard-n-primary-guards-to-use",
459 pub guard_dir_use_parallelism: BoundedInt32<1, {i32::MAX}> = (3)
463 from "guard-n-primary-dir-guards-to-use",
464
465 pub guard_nonprimary_connect_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (15)
469 from "guard-nonprimary-guard-connect-timeout",
470 pub guard_nonprimary_idle_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (600)
473 from "guard-nonprimary-guard-idle-timeout",
474 pub guard_remove_unlisted_after: IntegerDays<BoundedInt32<1,3650>> = (20)
477 from "guard-remove-unlisted-guards-after-days",
478
479
480 pub min_circuit_path_threshold: Percentage<BoundedInt32<25, 95>> = (60)
482 from "min_paths_for_circs_pct",
483
484 pub nf_ito_low: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (1500)
488 from "nf_ito_low",
489 pub nf_ito_high: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9500)
491 from "nf_ito_high",
492 pub nf_ito_low_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9000)
494 from "nf_ito_low_reduced",
495 pub nf_ito_high_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (14000)
497 from "nf_ito_high_reduced",
498
499 pub sendme_accept_min_version: SendMeVersion = (0)
501 from "sendme_accept_min_version",
502 pub sendme_emit_min_version: SendMeVersion = (0)
504 from "sendme_emit_min_version",
505
506 pub unused_client_circ_timeout: IntegerSeconds<BoundedInt32<60, 86_400>> = (30*60)
509 from "nf_conntimeout_clients",
510 pub unused_client_circ_timeout_while_learning_cbt: IntegerSeconds<BoundedInt32<10, 60_000>> = (3*60)
513 from "cbtlearntimeout",
514
515 pub hs_introcirc_requests_min: BoundedInt32<0, {i32::MAX}> = (16384)
519 from "hs_intro_min_introduce2",
520
521 pub hs_introcirc_requests_max: BoundedInt32<0, {i32::MAX}> = (32768)
525 from "hs_intro_max_introduce2",
526
527 pub hs_intro_min_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (18 * 60 * 60)
529 from "hs_intro_min_lifetime",
530
531 pub hs_intro_max_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (24 * 60 * 60)
533 from "hs_intro_max_lifetime",
534
535 pub hs_intro_num_extra_intropoints: BoundedInt32<0, 128> = (2)
538 from "hs_intro_num_extra",
539
540 pub hsdir_dl_max_reply_cells: BoundedInt32<2, 2304> = (110)
543 from "hsdir_dl_max_reply_cells",
544
545 pub hsdir_ul_max_reply_cells: BoundedInt32<2, 1024> = (8)
548 from "hsdir_ul_max_reply_cells",
549
550 pub hsdir_timeperiod_length: IntegerMinutes<BoundedInt32<5, 14400>> = (1440)
556 from "hsdir_interval",
557
558 pub hsdir_n_replicas: BoundedInt32<1, 16> = (2)
561 from "hsdir_n_replicas",
562
563 pub hsdir_spread_fetch: BoundedInt32<1, 128> = (3)
566 from "hsdir_spread_fetch",
567
568 pub hsdir_spread_store: BoundedInt32<1,128> = (4)
571 from "hsdir_spread_store",
572
573 pub hsdir_max_desc_size: BoundedInt32<1, {i32::MAX}> = (50_000)
575 from "HSV3MaxDescriptorSize",
576
577 pub hs_service_rendezvous_failures_max: BoundedInt32<1, 10> = (2)
580 from "hs_service_max_rdv_failures",
581
582 pub hs_intro_dos_enabled: BoundedInt32<0, 1> = (0)
587 from "HiddenServiceEnableIntroDoSDefense",
588
589 pub hs_intro_dos_max_burst: BoundedInt32<0, {i32::MAX}> = (200)
595 from "HiddenServiceEnableIntroDoSBurstPerSec",
596
597 pub hs_intro_dos_rate: BoundedInt32<0, {i32::MAX}> = (25)
603 from "HiddenServiceEnableIntroDoSRatePerSec",
604
605 pub hs_pow_v1_max_effort: BoundedInt32<0, {i32::MAX}> = (10_000)
612 from "HiddenServiceProofOfWorkV1MaxEffort",
613
614 pub hs_pow_v1_service_intro_timeout: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (300)
620 from "HiddenServiceProofOfWorkV1ServiceIntroTimeoutSeconds",
621
622 pub hs_pow_v1_default_decay_adjustment: Percentage<BoundedInt32<0, 99>> = (0)
627 from "HiddenServiceProofOfWorkV1ServiceDefaultDecayAdjustment",
628
629 pub vanguards_enabled: BoundedInt32<0, 2> = (1)
640 from "vanguards-enabled",
641
642 pub vanguards_hs_service: BoundedInt32<0, 2> = (2)
655 from "vanguards-hs-service",
656
657 pub guard_hs_l2_number: BoundedInt32<1, {i32::MAX}> = (4)
662 from "guard-hs-l2-number",
663
664 pub guard_hs_l2_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (86400)
669 from "guard-hs-l2-lifetime-min",
670
671 pub guard_hs_l2_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (1036800)
676 from "guard-hs-l2-lifetime-max",
677
678 pub guard_hs_l3_number: BoundedInt32<1, {i32::MAX}> = (8)
683 from "guard-hs-l3-number",
684
685 pub guard_hs_l3_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (3600)
690 from "guard-hs-l3-lifetime-min",
691
692 pub guard_hs_l3_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (172800)
697 from "guard-hs-l3-lifetime-max",
698
699 pub kist_enabled: BoundedInt32<0, 1> = (0)
710 from "kist-enabled",
711
712 pub kist_tcp_notsent_lowat: BoundedInt32<1, {i32::MAX}> = (1)
723 from "kist-tcp-notsent-lowat",
724
725 pub use_family_lists: BoundedInt32<0,1> = (1)
728 from "use-family-lists",
729
730 pub use_family_ids: BoundedInt32<0,1> = (1)
733 from "use-family-ids",
734}
735
736}
737
738impl Default for NetParameters {
739 fn default() -> Self {
740 NetParameters::default_values().expect("Default parameters were out-of-bounds")
741 }
742}
743
744impl AsRef<NetParameters> for NetParameters {
747 fn as_ref(&self) -> &NetParameters {
748 self
749 }
750}
751
752impl NetParameters {
753 pub fn from_map(p: &tor_netdoc::doc::netstatus::NetParams<i32>) -> Self {
757 let mut params = NetParameters::default();
758 let unrecognized = params.saturating_update(p.iter());
759 for u in unrecognized {
760 tracing::debug!("Ignored unrecognized net param: {u}");
761 }
762 params
763 }
764
765 pub(crate) fn saturating_update<'a, S>(
770 &mut self,
771 iter: impl Iterator<Item = (S, &'a i32)>,
772 ) -> Vec<S>
773 where
774 S: AsRef<str>,
775 {
776 let mut unrecognized = Vec::new();
777 for (k, v) in iter {
778 if !self.set_saturating(k.as_ref(), *v) {
779 unrecognized.push(k);
780 }
781 }
782 unrecognized
783 }
784}
785
786#[cfg(test)]
787#[allow(clippy::many_single_char_names)]
788#[allow(clippy::cognitive_complexity)]
789mod test {
790 #![allow(clippy::bool_assert_comparison)]
792 #![allow(clippy::clone_on_copy)]
793 #![allow(clippy::dbg_macro)]
794 #![allow(clippy::mixed_attributes_style)]
795 #![allow(clippy::print_stderr)]
796 #![allow(clippy::print_stdout)]
797 #![allow(clippy::single_char_pattern)]
798 #![allow(clippy::unwrap_used)]
799 #![allow(clippy::unchecked_duration_subtraction)]
800 #![allow(clippy::useless_vec)]
801 #![allow(clippy::needless_pass_by_value)]
802 use super::*;
804 use std::string::String;
805
806 #[test]
807 fn empty_list() {
808 let mut x = NetParameters::default();
809 let y = Vec::<(&String, &i32)>::new();
810 let u = x.saturating_update(y.into_iter());
811 assert!(u.is_empty());
812 }
813
814 #[test]
815 fn unknown_parameter() {
816 let mut x = NetParameters::default();
817 let mut y = Vec::<(&String, &i32)>::new();
818 let k = &String::from("This_is_not_a_real_key");
819 let v = &456;
820 y.push((k, v));
821 let u = x.saturating_update(y.into_iter());
822 assert_eq!(u, vec![&String::from("This_is_not_a_real_key")]);
823 }
824
825 #[test]
829 fn single_good_parameter() {
830 let mut x = NetParameters::default();
831 let mut y = Vec::<(&String, &i32)>::new();
832 let k = &String::from("min_paths_for_circs_pct");
833 let v = &54;
834 y.push((k, v));
835 let z = x.saturating_update(y.into_iter());
836 assert!(z.is_empty());
837 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
838 }
839
840 #[test]
841 fn multiple_good_parameters() {
842 let mut x = NetParameters::default();
843 let mut y = Vec::<(&String, &i32)>::new();
844 let k = &String::from("min_paths_for_circs_pct");
845 let v = &54;
846 y.push((k, v));
847 let k = &String::from("circwindow");
848 let v = &900;
849 y.push((k, v));
850 let z = x.saturating_update(y.into_iter());
851 assert!(z.is_empty());
852 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
853 assert_eq!(x.circuit_window.get(), 900);
854 }
855
856 #[test]
857 fn good_out_of_range() {
858 let mut x = NetParameters::default();
859 let mut y = Vec::<(&String, &i32)>::new();
860 let k = &String::from("sendme_accept_min_version");
861 let v = &30;
862 y.push((k, v));
863 let k = &String::from("min_paths_for_circs_pct");
864 let v = &255;
865 y.push((k, v));
866 let z = x.saturating_update(y.into_iter());
867 assert!(z.is_empty());
868 assert_eq!(x.sendme_accept_min_version.get(), 30);
869 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
870 }
871
872 #[test]
873 fn good_invalid_rep() {
874 let mut x = NetParameters::default();
875 let mut y = Vec::<(&String, &i32)>::new();
876 let k = &String::from("sendme_accept_min_version");
877 let v = &30;
878 y.push((k, v));
879 let k = &String::from("min_paths_for_circs_pct");
880 let v = &9000;
881 y.push((k, v));
882 let z = x.saturating_update(y.into_iter());
883 assert!(z.is_empty());
884 assert_eq!(x.sendme_accept_min_version.get(), 30);
885 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
886 }
887
888 #[test]
891 fn good_unknown() {
892 let mut x = NetParameters::default();
893 let mut y = Vec::<(&String, &i32)>::new();
894 let k = &String::from("sendme_accept_min_version");
895 let v = &30;
896 y.push((k, v));
897 let k = &String::from("not_a_real_parameter");
898 let v = &9000;
899 y.push((k, v));
900 let z = x.saturating_update(y.into_iter());
901 assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
902 assert_eq!(x.sendme_accept_min_version.get(), 30);
903 }
904
905 #[test]
906 fn from_consensus() {
907 let mut p = NetParameters::default();
908 let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
909 mp.insert("bwweightscale".to_string(), 70);
910 mp.insert("min_paths_for_circs_pct".to_string(), 45);
911 mp.insert("im_a_little_teapot".to_string(), 1);
912 mp.insert("circwindow".to_string(), 99999);
913 mp.insert("ExtendByEd25519ID".to_string(), 1);
914
915 let z = p.saturating_update(mp.iter());
916 assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
917
918 assert_eq!(p.bw_weight_scale.get(), 70);
919 assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
920 let b_val: bool = p.extend_by_ed25519_id.into();
921 assert!(b_val);
922 }
923
924 #[test]
925 fn all_parameters() {
926 use std::time::Duration;
927 let mut p = NetParameters::default();
928 let mp = [
929 ("bwweightscale", 10),
930 ("cbtdisabled", 1),
931 ("cbtnummodes", 11),
932 ("cbtrecentcount", 12),
933 ("cbtmaxtimeouts", 13),
934 ("cbtmincircs", 5),
935 ("cbtquantile", 61),
936 ("cbtclosequantile", 15),
937 ("cbtlearntimeout", 1900),
938 ("cbtmintimeout", 2020),
939 ("cbtinitialtimeout", 2050),
940 ("cbttestfreq", 110),
941 ("cbtmaxopencircs", 14),
942 ("circwindow", 999),
943 ("CircuitPriorityHalflifeMsec", 222),
944 ("guard-lifetime-days", 36),
945 ("guard-confirmed-min-lifetime-days", 37),
946 ("guard-internet-likely-down-interval", 38),
947 ("guard-max-sample-size", 39),
948 ("guard-max-sample-threshold", 40),
949 ("guard-min-filtered-sample-size", 41),
950 ("guard-n-primary-guards", 42),
951 ("guard-n-primary-guards-to-use", 43),
952 ("guard-n-primary-dir-guards-to-use", 44),
953 ("guard-nonprimary-guard-connect-timeout", 45),
954 ("guard-nonprimary-guard-idle-timeout", 46),
955 ("guard-remove-unlisted-guards-after-days", 47),
956 ("guard-meaningful-restriction-percent", 12),
957 ("guard-extreme-restriction-percent", 3),
958 ("ExtendByEd25519ID", 0),
959 ("min_paths_for_circs_pct", 51),
960 ("nf_conntimeout_clients", 606),
961 ("nf_ito_low", 1_000),
962 ("nf_ito_high", 20_000),
963 ("nf_ito_low_reduced", 3_000),
964 ("nf_ito_high_reduced", 40_000),
965 ("sendme_accept_min_version", 31),
966 ("sendme_emit_min_version", 32),
967 ];
968 let ignored = p.saturating_update(mp.iter().map(|(a, b)| (a, b)));
969 assert!(ignored.is_empty());
970
971 assert_eq!(p.bw_weight_scale.get(), 10);
972 assert!(bool::from(p.cbt_learning_disabled));
973 assert_eq!(p.cbt_num_xm_modes.get(), 11);
974 assert_eq!(p.cbt_success_count.get(), 12);
975 assert_eq!(p.cbt_max_timeouts.get(), 13);
976 assert_eq!(p.cbt_min_circs_for_estimate.get(), 5);
977 assert_eq!(p.cbt_timeout_quantile.as_percent().get(), 61);
978 assert_eq!(p.cbt_abandon_quantile.as_percent().get(), 15);
979 assert_eq!(p.nf_ito_low.as_millis().get(), 1_000);
980 assert_eq!(p.nf_ito_high.as_millis().get(), 20_000);
981 assert_eq!(p.nf_ito_low_reduced.as_millis().get(), 3_000);
982 assert_eq!(p.nf_ito_high_reduced.as_millis().get(), 40_000);
983 assert_eq!(
984 Duration::try_from(p.unused_client_circ_timeout_while_learning_cbt).unwrap(),
985 Duration::from_secs(1900)
986 );
987 assert_eq!(
988 Duration::try_from(p.cbt_min_timeout).unwrap(),
989 Duration::from_millis(2020)
990 );
991 assert_eq!(
992 Duration::try_from(p.cbt_initial_timeout).unwrap(),
993 Duration::from_millis(2050)
994 );
995 assert_eq!(
996 Duration::try_from(p.cbt_testing_delay).unwrap(),
997 Duration::from_secs(110)
998 );
999 assert_eq!(p.cbt_max_open_circuits_for_testing.get(), 14);
1000 assert_eq!(p.circuit_window.get(), 999);
1001 assert_eq!(
1002 Duration::try_from(p.circuit_priority_half_life).unwrap(),
1003 Duration::from_millis(222)
1004 );
1005 assert!(!bool::from(p.extend_by_ed25519_id));
1006 assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 51);
1007 assert_eq!(
1008 Duration::try_from(p.unused_client_circ_timeout).unwrap(),
1009 Duration::from_secs(606)
1010 );
1011 assert_eq!(p.sendme_accept_min_version.get(), 31);
1012 assert_eq!(p.sendme_emit_min_version.get(), 32);
1013
1014 assert_eq!(
1015 Duration::try_from(p.guard_lifetime_unconfirmed).unwrap(),
1016 Duration::from_secs(86400 * 36)
1017 );
1018 assert_eq!(
1019 Duration::try_from(p.guard_lifetime_confirmed).unwrap(),
1020 Duration::from_secs(86400 * 37)
1021 );
1022 assert_eq!(
1023 Duration::try_from(p.guard_internet_likely_down).unwrap(),
1024 Duration::from_secs(38)
1025 );
1026 assert_eq!(p.guard_max_sample_size.get(), 39);
1027 assert_eq!(p.guard_max_sample_threshold.as_percent().get(), 40);
1028 assert_eq!(p.guard_filtered_min_sample_size.get(), 41);
1029 assert_eq!(p.guard_n_primary.get(), 42);
1030 assert_eq!(p.guard_use_parallelism.get(), 43);
1031 assert_eq!(p.guard_dir_use_parallelism.get(), 44);
1032 assert_eq!(
1033 Duration::try_from(p.guard_nonprimary_connect_timeout).unwrap(),
1034 Duration::from_secs(45)
1035 );
1036 assert_eq!(
1037 Duration::try_from(p.guard_nonprimary_idle_timeout).unwrap(),
1038 Duration::from_secs(46)
1039 );
1040 assert_eq!(
1041 Duration::try_from(p.guard_remove_unlisted_after).unwrap(),
1042 Duration::from_secs(86400 * 47)
1043 );
1044 assert_eq!(p.guard_meaningful_restriction.as_percent().get(), 12);
1045 assert_eq!(p.guard_extreme_restriction.as_percent().get(), 3);
1046 }
1047}