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 onion_key_rotation_days: BoundedInt32<1, 90> = (28)
412 from "onion-key-rotation-days",
413 pub onion_key_grace_period_days: BoundedInt32<1, 90> = (7)
417 from "onion-key-grace-period-days",
418
419 pub guard_meaningful_restriction: Percentage<BoundedInt32<1,100>> = (20)
423 from "guard-meaningful-restriction-percent",
424
425 pub guard_extreme_restriction: Percentage<BoundedInt32<1,100>> = (1)
428 from "guard-extreme-restriction-percent",
429
430 pub guard_lifetime_unconfirmed: IntegerDays<BoundedInt32<1, 3650>> = (120)
433 from "guard-lifetime-days",
434
435 pub guard_lifetime_confirmed: IntegerDays<BoundedInt32<1, 3650>> = (60)
438 from "guard-confirmed-min-lifetime-days",
439
440 pub guard_internet_likely_down: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (600)
444 from "guard-internet-likely-down-interval",
445 pub guard_max_sample_size: BoundedInt32<1, {i32::MAX}> = (60)
448 from "guard-max-sample-size",
449 pub guard_max_sample_threshold: Percentage<BoundedInt32<1,100>> = (20)
452 from "guard-max-sample-threshold",
453
454 pub guard_filtered_min_sample_size: BoundedInt32<1,{i32::MAX}> = (20)
458 from "guard-min-filtered-sample-size",
459
460 pub guard_n_primary: BoundedInt32<1,{i32::MAX}> = (3)
463 from "guard-n-primary-guards",
464 pub guard_use_parallelism: BoundedInt32<1, {i32::MAX}> = (1)
467 from "guard-n-primary-guards-to-use",
468 pub guard_dir_use_parallelism: BoundedInt32<1, {i32::MAX}> = (3)
472 from "guard-n-primary-dir-guards-to-use",
473
474 pub guard_nonprimary_connect_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (15)
478 from "guard-nonprimary-guard-connect-timeout",
479 pub guard_nonprimary_idle_timeout: IntegerSeconds<BoundedInt32<1,{i32::MAX}>> = (600)
482 from "guard-nonprimary-guard-idle-timeout",
483 pub guard_remove_unlisted_after: IntegerDays<BoundedInt32<1,3650>> = (20)
486 from "guard-remove-unlisted-guards-after-days",
487
488
489 pub min_circuit_path_threshold: Percentage<BoundedInt32<25, 95>> = (60)
491 from "min_paths_for_circs_pct",
492
493 pub nf_ito_low: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (1500)
497 from "nf_ito_low",
498 pub nf_ito_high: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9500)
500 from "nf_ito_high",
501 pub nf_ito_low_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (9000)
503 from "nf_ito_low_reduced",
504 pub nf_ito_high_reduced: IntegerMilliseconds<BoundedInt32<0, CHANNEL_PADDING_TIMEOUT_UPPER_BOUND>> = (14000)
506 from "nf_ito_high_reduced",
507
508 pub sendme_accept_min_version: SendMeVersion = (0)
510 from "sendme_accept_min_version",
511 pub sendme_emit_min_version: SendMeVersion = (0)
513 from "sendme_emit_min_version",
514
515 pub unused_client_circ_timeout: IntegerSeconds<BoundedInt32<60, 86_400>> = (30*60)
518 from "nf_conntimeout_clients",
519 pub unused_client_circ_timeout_while_learning_cbt: IntegerSeconds<BoundedInt32<10, 60_000>> = (3*60)
522 from "cbtlearntimeout",
523
524 pub hs_introcirc_requests_min: BoundedInt32<0, {i32::MAX}> = (16384)
528 from "hs_intro_min_introduce2",
529
530 pub hs_introcirc_requests_max: BoundedInt32<0, {i32::MAX}> = (32768)
534 from "hs_intro_max_introduce2",
535
536 pub hs_intro_min_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (18 * 60 * 60)
538 from "hs_intro_min_lifetime",
539
540 pub hs_intro_max_lifetime: IntegerSeconds<BoundedInt32<0, {i32::MAX}>> = (24 * 60 * 60)
542 from "hs_intro_max_lifetime",
543
544 pub hs_intro_num_extra_intropoints: BoundedInt32<0, 128> = (2)
547 from "hs_intro_num_extra",
548
549 pub hsdir_dl_max_reply_cells: BoundedInt32<2, 2304> = (110)
552 from "hsdir_dl_max_reply_cells",
553
554 pub hsdir_ul_max_reply_cells: BoundedInt32<2, 1024> = (8)
557 from "hsdir_ul_max_reply_cells",
558
559 pub hsdir_timeperiod_length: IntegerMinutes<BoundedInt32<5, 14400>> = (1440)
565 from "hsdir_interval",
566
567 pub hsdir_n_replicas: BoundedInt32<1, 16> = (2)
570 from "hsdir_n_replicas",
571
572 pub hsdir_spread_fetch: BoundedInt32<1, 128> = (3)
575 from "hsdir_spread_fetch",
576
577 pub hsdir_spread_store: BoundedInt32<1,128> = (4)
580 from "hsdir_spread_store",
581
582 pub hsdir_max_desc_size: BoundedInt32<1, {i32::MAX}> = (50_000)
584 from "HSV3MaxDescriptorSize",
585
586 pub hs_service_rendezvous_failures_max: BoundedInt32<1, 10> = (2)
589 from "hs_service_max_rdv_failures",
590
591 pub hs_intro_dos_enabled: BoundedInt32<0, 1> = (0)
596 from "HiddenServiceEnableIntroDoSDefense",
597
598 pub hs_intro_dos_max_burst: BoundedInt32<0, {i32::MAX}> = (200)
604 from "HiddenServiceEnableIntroDoSBurstPerSec",
605
606 pub hs_intro_dos_rate: BoundedInt32<0, {i32::MAX}> = (25)
612 from "HiddenServiceEnableIntroDoSRatePerSec",
613
614 pub hs_pow_v1_max_effort: BoundedInt32<0, {i32::MAX}> = (10_000)
621 from "HiddenServiceProofOfWorkV1MaxEffort",
622
623 pub hs_pow_v1_service_intro_timeout: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (300)
629 from "HiddenServiceProofOfWorkV1ServiceIntroTimeoutSeconds",
630
631 pub hs_pow_v1_default_decay_adjustment: Percentage<BoundedInt32<0, 99>> = (0)
636 from "HiddenServiceProofOfWorkV1ServiceDefaultDecayAdjustment",
637
638 pub vanguards_enabled: BoundedInt32<0, 2> = (1)
649 from "vanguards-enabled",
650
651 pub vanguards_hs_service: BoundedInt32<0, 2> = (2)
664 from "vanguards-hs-service",
665
666 pub guard_hs_l2_number: BoundedInt32<1, {i32::MAX}> = (4)
671 from "guard-hs-l2-number",
672
673 pub guard_hs_l2_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (86400)
678 from "guard-hs-l2-lifetime-min",
679
680 pub guard_hs_l2_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (1036800)
685 from "guard-hs-l2-lifetime-max",
686
687 pub guard_hs_l3_number: BoundedInt32<1, {i32::MAX}> = (8)
692 from "guard-hs-l3-number",
693
694 pub guard_hs_l3_lifetime_min: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (3600)
699 from "guard-hs-l3-lifetime-min",
700
701 pub guard_hs_l3_lifetime_max: IntegerSeconds<BoundedInt32<1, {i32::MAX}>> = (172800)
706 from "guard-hs-l3-lifetime-max",
707
708 pub kist_enabled: BoundedInt32<0, 1> = (0)
719 from "kist-enabled",
720
721 pub kist_tcp_notsent_lowat: BoundedInt32<1, {i32::MAX}> = (1)
732 from "kist-tcp-notsent-lowat",
733
734 pub use_family_lists: BoundedInt32<0,1> = (1)
737 from "use-family-lists",
738
739 pub use_family_ids: BoundedInt32<0,1> = (1)
742 from "use-family-ids",
743}
744
745}
746
747impl Default for NetParameters {
748 fn default() -> Self {
749 NetParameters::default_values().expect("Default parameters were out-of-bounds")
750 }
751}
752
753impl AsRef<NetParameters> for NetParameters {
756 fn as_ref(&self) -> &NetParameters {
757 self
758 }
759}
760
761impl NetParameters {
762 pub fn from_map(p: &tor_netdoc::doc::netstatus::NetParams<i32>) -> Self {
766 let mut params = NetParameters::default();
767 let unrecognized = params.saturating_update(p.iter());
768 for u in unrecognized {
769 tracing::debug!("Ignored unrecognized net param: {u}");
770 }
771 params
772 }
773
774 pub(crate) fn saturating_update<'a, S>(
779 &mut self,
780 iter: impl Iterator<Item = (S, &'a i32)>,
781 ) -> Vec<S>
782 where
783 S: AsRef<str>,
784 {
785 let mut unrecognized = Vec::new();
786 for (k, v) in iter {
787 if !self.set_saturating(k.as_ref(), *v) {
788 unrecognized.push(k);
789 }
790 }
791 unrecognized
792 }
793}
794
795#[cfg(test)]
796#[allow(clippy::many_single_char_names)]
797#[allow(clippy::cognitive_complexity)]
798mod test {
799 #![allow(clippy::bool_assert_comparison)]
801 #![allow(clippy::clone_on_copy)]
802 #![allow(clippy::dbg_macro)]
803 #![allow(clippy::mixed_attributes_style)]
804 #![allow(clippy::print_stderr)]
805 #![allow(clippy::print_stdout)]
806 #![allow(clippy::single_char_pattern)]
807 #![allow(clippy::unwrap_used)]
808 #![allow(clippy::unchecked_time_subtraction)]
809 #![allow(clippy::useless_vec)]
810 #![allow(clippy::needless_pass_by_value)]
811 use super::*;
813 use std::string::String;
814
815 #[test]
816 fn empty_list() {
817 let mut x = NetParameters::default();
818 let y = Vec::<(&String, &i32)>::new();
819 let u = x.saturating_update(y.into_iter());
820 assert!(u.is_empty());
821 }
822
823 #[test]
824 fn unknown_parameter() {
825 let mut x = NetParameters::default();
826 let mut y = Vec::<(&String, &i32)>::new();
827 let k = &String::from("This_is_not_a_real_key");
828 let v = &456;
829 y.push((k, v));
830 let u = x.saturating_update(y.into_iter());
831 assert_eq!(u, vec![&String::from("This_is_not_a_real_key")]);
832 }
833
834 #[test]
838 fn single_good_parameter() {
839 let mut x = NetParameters::default();
840 let mut y = Vec::<(&String, &i32)>::new();
841 let k = &String::from("min_paths_for_circs_pct");
842 let v = &54;
843 y.push((k, v));
844 let z = x.saturating_update(y.into_iter());
845 assert!(z.is_empty());
846 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
847 }
848
849 #[test]
850 fn multiple_good_parameters() {
851 let mut x = NetParameters::default();
852 let mut y = Vec::<(&String, &i32)>::new();
853 let k = &String::from("min_paths_for_circs_pct");
854 let v = &54;
855 y.push((k, v));
856 let k = &String::from("circwindow");
857 let v = &900;
858 y.push((k, v));
859 let z = x.saturating_update(y.into_iter());
860 assert!(z.is_empty());
861 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
862 assert_eq!(x.circuit_window.get(), 900);
863 }
864
865 #[test]
866 fn good_out_of_range() {
867 let mut x = NetParameters::default();
868 let mut y = Vec::<(&String, &i32)>::new();
869 let k = &String::from("sendme_accept_min_version");
870 let v = &30;
871 y.push((k, v));
872 let k = &String::from("min_paths_for_circs_pct");
873 let v = &255;
874 y.push((k, v));
875 let z = x.saturating_update(y.into_iter());
876 assert!(z.is_empty());
877 assert_eq!(x.sendme_accept_min_version.get(), 30);
878 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
879 }
880
881 #[test]
882 fn good_invalid_rep() {
883 let mut x = NetParameters::default();
884 let mut y = Vec::<(&String, &i32)>::new();
885 let k = &String::from("sendme_accept_min_version");
886 let v = &30;
887 y.push((k, v));
888 let k = &String::from("min_paths_for_circs_pct");
889 let v = &9000;
890 y.push((k, v));
891 let z = x.saturating_update(y.into_iter());
892 assert!(z.is_empty());
893 assert_eq!(x.sendme_accept_min_version.get(), 30);
894 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
895 }
896
897 #[test]
900 fn good_unknown() {
901 let mut x = NetParameters::default();
902 let mut y = Vec::<(&String, &i32)>::new();
903 let k = &String::from("sendme_accept_min_version");
904 let v = &30;
905 y.push((k, v));
906 let k = &String::from("not_a_real_parameter");
907 let v = &9000;
908 y.push((k, v));
909 let z = x.saturating_update(y.into_iter());
910 assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
911 assert_eq!(x.sendme_accept_min_version.get(), 30);
912 }
913
914 #[test]
915 fn from_consensus() {
916 let mut p = NetParameters::default();
917 let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
918 mp.insert("bwweightscale".to_string(), 70);
919 mp.insert("min_paths_for_circs_pct".to_string(), 45);
920 mp.insert("im_a_little_teapot".to_string(), 1);
921 mp.insert("circwindow".to_string(), 99999);
922 mp.insert("ExtendByEd25519ID".to_string(), 1);
923
924 let z = p.saturating_update(mp.iter());
925 assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
926
927 assert_eq!(p.bw_weight_scale.get(), 70);
928 assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
929 let b_val: bool = p.extend_by_ed25519_id.into();
930 assert!(b_val);
931 }
932
933 #[test]
934 fn all_parameters() {
935 use std::time::Duration;
936 let mut p = NetParameters::default();
937 let mp = [
938 ("bwweightscale", 10),
939 ("cbtdisabled", 1),
940 ("cbtnummodes", 11),
941 ("cbtrecentcount", 12),
942 ("cbtmaxtimeouts", 13),
943 ("cbtmincircs", 5),
944 ("cbtquantile", 61),
945 ("cbtclosequantile", 15),
946 ("cbtlearntimeout", 1900),
947 ("cbtmintimeout", 2020),
948 ("cbtinitialtimeout", 2050),
949 ("cbttestfreq", 110),
950 ("cbtmaxopencircs", 14),
951 ("circwindow", 999),
952 ("CircuitPriorityHalflifeMsec", 222),
953 ("guard-lifetime-days", 36),
954 ("guard-confirmed-min-lifetime-days", 37),
955 ("guard-internet-likely-down-interval", 38),
956 ("guard-max-sample-size", 39),
957 ("guard-max-sample-threshold", 40),
958 ("guard-min-filtered-sample-size", 41),
959 ("guard-n-primary-guards", 42),
960 ("guard-n-primary-guards-to-use", 43),
961 ("guard-n-primary-dir-guards-to-use", 44),
962 ("guard-nonprimary-guard-connect-timeout", 45),
963 ("guard-nonprimary-guard-idle-timeout", 46),
964 ("guard-remove-unlisted-guards-after-days", 47),
965 ("guard-meaningful-restriction-percent", 12),
966 ("guard-extreme-restriction-percent", 3),
967 ("ExtendByEd25519ID", 0),
968 ("min_paths_for_circs_pct", 51),
969 ("nf_conntimeout_clients", 606),
970 ("nf_ito_low", 1_000),
971 ("nf_ito_high", 20_000),
972 ("nf_ito_low_reduced", 3_000),
973 ("nf_ito_high_reduced", 40_000),
974 ("sendme_accept_min_version", 31),
975 ("sendme_emit_min_version", 32),
976 ];
977 let ignored = p.saturating_update(mp.iter().map(|(a, b)| (a, b)));
978 assert!(ignored.is_empty());
979
980 assert_eq!(p.bw_weight_scale.get(), 10);
981 assert!(bool::from(p.cbt_learning_disabled));
982 assert_eq!(p.cbt_num_xm_modes.get(), 11);
983 assert_eq!(p.cbt_success_count.get(), 12);
984 assert_eq!(p.cbt_max_timeouts.get(), 13);
985 assert_eq!(p.cbt_min_circs_for_estimate.get(), 5);
986 assert_eq!(p.cbt_timeout_quantile.as_percent().get(), 61);
987 assert_eq!(p.cbt_abandon_quantile.as_percent().get(), 15);
988 assert_eq!(p.nf_ito_low.as_millis().get(), 1_000);
989 assert_eq!(p.nf_ito_high.as_millis().get(), 20_000);
990 assert_eq!(p.nf_ito_low_reduced.as_millis().get(), 3_000);
991 assert_eq!(p.nf_ito_high_reduced.as_millis().get(), 40_000);
992 assert_eq!(
993 Duration::try_from(p.unused_client_circ_timeout_while_learning_cbt).unwrap(),
994 Duration::from_secs(1900)
995 );
996 assert_eq!(
997 Duration::try_from(p.cbt_min_timeout).unwrap(),
998 Duration::from_millis(2020)
999 );
1000 assert_eq!(
1001 Duration::try_from(p.cbt_initial_timeout).unwrap(),
1002 Duration::from_millis(2050)
1003 );
1004 assert_eq!(
1005 Duration::try_from(p.cbt_testing_delay).unwrap(),
1006 Duration::from_secs(110)
1007 );
1008 assert_eq!(p.cbt_max_open_circuits_for_testing.get(), 14);
1009 assert_eq!(p.circuit_window.get(), 999);
1010 assert_eq!(
1011 Duration::try_from(p.circuit_priority_half_life).unwrap(),
1012 Duration::from_millis(222)
1013 );
1014 assert!(!bool::from(p.extend_by_ed25519_id));
1015 assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 51);
1016 assert_eq!(
1017 Duration::try_from(p.unused_client_circ_timeout).unwrap(),
1018 Duration::from_secs(606)
1019 );
1020 assert_eq!(p.sendme_accept_min_version.get(), 31);
1021 assert_eq!(p.sendme_emit_min_version.get(), 32);
1022
1023 assert_eq!(
1024 Duration::try_from(p.guard_lifetime_unconfirmed).unwrap(),
1025 Duration::from_secs(86400 * 36)
1026 );
1027 assert_eq!(
1028 Duration::try_from(p.guard_lifetime_confirmed).unwrap(),
1029 Duration::from_secs(86400 * 37)
1030 );
1031 assert_eq!(
1032 Duration::try_from(p.guard_internet_likely_down).unwrap(),
1033 Duration::from_secs(38)
1034 );
1035 assert_eq!(p.guard_max_sample_size.get(), 39);
1036 assert_eq!(p.guard_max_sample_threshold.as_percent().get(), 40);
1037 assert_eq!(p.guard_filtered_min_sample_size.get(), 41);
1038 assert_eq!(p.guard_n_primary.get(), 42);
1039 assert_eq!(p.guard_use_parallelism.get(), 43);
1040 assert_eq!(p.guard_dir_use_parallelism.get(), 44);
1041 assert_eq!(
1042 Duration::try_from(p.guard_nonprimary_connect_timeout).unwrap(),
1043 Duration::from_secs(45)
1044 );
1045 assert_eq!(
1046 Duration::try_from(p.guard_nonprimary_idle_timeout).unwrap(),
1047 Duration::from_secs(46)
1048 );
1049 assert_eq!(
1050 Duration::try_from(p.guard_remove_unlisted_after).unwrap(),
1051 Duration::from_secs(86400 * 47)
1052 );
1053 assert_eq!(p.guard_meaningful_restriction.as_percent().get(), 12);
1054 assert_eq!(p.guard_extreme_restriction.as_percent().get(), 3);
1055 }
1056}