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 #![allow(clippy::string_slice)] use super::*;
814 use std::string::String;
815
816 #[test]
817 fn empty_list() {
818 let mut x = NetParameters::default();
819 let y = Vec::<(&String, &i32)>::new();
820 let u = x.saturating_update(y.into_iter());
821 assert!(u.is_empty());
822 }
823
824 #[test]
825 fn unknown_parameter() {
826 let mut x = NetParameters::default();
827 let mut y = Vec::<(&String, &i32)>::new();
828 let k = &String::from("This_is_not_a_real_key");
829 let v = &456;
830 y.push((k, v));
831 let u = x.saturating_update(y.into_iter());
832 assert_eq!(u, vec![&String::from("This_is_not_a_real_key")]);
833 }
834
835 #[test]
839 fn single_good_parameter() {
840 let mut x = NetParameters::default();
841 let mut y = Vec::<(&String, &i32)>::new();
842 let k = &String::from("min_paths_for_circs_pct");
843 let v = &54;
844 y.push((k, v));
845 let z = x.saturating_update(y.into_iter());
846 assert!(z.is_empty());
847 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
848 }
849
850 #[test]
851 fn multiple_good_parameters() {
852 let mut x = NetParameters::default();
853 let mut y = Vec::<(&String, &i32)>::new();
854 let k = &String::from("min_paths_for_circs_pct");
855 let v = &54;
856 y.push((k, v));
857 let k = &String::from("circwindow");
858 let v = &900;
859 y.push((k, v));
860 let z = x.saturating_update(y.into_iter());
861 assert!(z.is_empty());
862 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
863 assert_eq!(x.circuit_window.get(), 900);
864 }
865
866 #[test]
867 fn good_out_of_range() {
868 let mut x = NetParameters::default();
869 let mut y = Vec::<(&String, &i32)>::new();
870 let k = &String::from("sendme_accept_min_version");
871 let v = &30;
872 y.push((k, v));
873 let k = &String::from("min_paths_for_circs_pct");
874 let v = &255;
875 y.push((k, v));
876 let z = x.saturating_update(y.into_iter());
877 assert!(z.is_empty());
878 assert_eq!(x.sendme_accept_min_version.get(), 30);
879 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
880 }
881
882 #[test]
883 fn good_invalid_rep() {
884 let mut x = NetParameters::default();
885 let mut y = Vec::<(&String, &i32)>::new();
886 let k = &String::from("sendme_accept_min_version");
887 let v = &30;
888 y.push((k, v));
889 let k = &String::from("min_paths_for_circs_pct");
890 let v = &9000;
891 y.push((k, v));
892 let z = x.saturating_update(y.into_iter());
893 assert!(z.is_empty());
894 assert_eq!(x.sendme_accept_min_version.get(), 30);
895 assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
896 }
897
898 #[test]
901 fn good_unknown() {
902 let mut x = NetParameters::default();
903 let mut y = Vec::<(&String, &i32)>::new();
904 let k = &String::from("sendme_accept_min_version");
905 let v = &30;
906 y.push((k, v));
907 let k = &String::from("not_a_real_parameter");
908 let v = &9000;
909 y.push((k, v));
910 let z = x.saturating_update(y.into_iter());
911 assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
912 assert_eq!(x.sendme_accept_min_version.get(), 30);
913 }
914
915 #[test]
916 fn from_consensus() {
917 let mut p = NetParameters::default();
918 let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
919 mp.insert("bwweightscale".to_string(), 70);
920 mp.insert("min_paths_for_circs_pct".to_string(), 45);
921 mp.insert("im_a_little_teapot".to_string(), 1);
922 mp.insert("circwindow".to_string(), 99999);
923 mp.insert("ExtendByEd25519ID".to_string(), 1);
924
925 let z = p.saturating_update(mp.iter());
926 assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
927
928 assert_eq!(p.bw_weight_scale.get(), 70);
929 assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
930 let b_val: bool = p.extend_by_ed25519_id.into();
931 assert!(b_val);
932 }
933
934 #[test]
935 fn all_parameters() {
936 use std::time::Duration;
937 let mut p = NetParameters::default();
938 let mp = [
939 ("bwweightscale", 10),
940 ("cbtdisabled", 1),
941 ("cbtnummodes", 11),
942 ("cbtrecentcount", 12),
943 ("cbtmaxtimeouts", 13),
944 ("cbtmincircs", 5),
945 ("cbtquantile", 61),
946 ("cbtclosequantile", 15),
947 ("cbtlearntimeout", 1900),
948 ("cbtmintimeout", 2020),
949 ("cbtinitialtimeout", 2050),
950 ("cbttestfreq", 110),
951 ("cbtmaxopencircs", 14),
952 ("circwindow", 999),
953 ("CircuitPriorityHalflifeMsec", 222),
954 ("guard-lifetime-days", 36),
955 ("guard-confirmed-min-lifetime-days", 37),
956 ("guard-internet-likely-down-interval", 38),
957 ("guard-max-sample-size", 39),
958 ("guard-max-sample-threshold", 40),
959 ("guard-min-filtered-sample-size", 41),
960 ("guard-n-primary-guards", 42),
961 ("guard-n-primary-guards-to-use", 43),
962 ("guard-n-primary-dir-guards-to-use", 44),
963 ("guard-nonprimary-guard-connect-timeout", 45),
964 ("guard-nonprimary-guard-idle-timeout", 46),
965 ("guard-remove-unlisted-guards-after-days", 47),
966 ("guard-meaningful-restriction-percent", 12),
967 ("guard-extreme-restriction-percent", 3),
968 ("ExtendByEd25519ID", 0),
969 ("min_paths_for_circs_pct", 51),
970 ("nf_conntimeout_clients", 606),
971 ("nf_ito_low", 1_000),
972 ("nf_ito_high", 20_000),
973 ("nf_ito_low_reduced", 3_000),
974 ("nf_ito_high_reduced", 40_000),
975 ("sendme_accept_min_version", 31),
976 ("sendme_emit_min_version", 32),
977 ];
978 let ignored = p.saturating_update(mp.iter().map(|(a, b)| (a, b)));
979 assert!(ignored.is_empty());
980
981 assert_eq!(p.bw_weight_scale.get(), 10);
982 assert!(bool::from(p.cbt_learning_disabled));
983 assert_eq!(p.cbt_num_xm_modes.get(), 11);
984 assert_eq!(p.cbt_success_count.get(), 12);
985 assert_eq!(p.cbt_max_timeouts.get(), 13);
986 assert_eq!(p.cbt_min_circs_for_estimate.get(), 5);
987 assert_eq!(p.cbt_timeout_quantile.as_percent().get(), 61);
988 assert_eq!(p.cbt_abandon_quantile.as_percent().get(), 15);
989 assert_eq!(p.nf_ito_low.as_millis().get(), 1_000);
990 assert_eq!(p.nf_ito_high.as_millis().get(), 20_000);
991 assert_eq!(p.nf_ito_low_reduced.as_millis().get(), 3_000);
992 assert_eq!(p.nf_ito_high_reduced.as_millis().get(), 40_000);
993 assert_eq!(
994 Duration::try_from(p.unused_client_circ_timeout_while_learning_cbt).unwrap(),
995 Duration::from_secs(1900)
996 );
997 assert_eq!(
998 Duration::try_from(p.cbt_min_timeout).unwrap(),
999 Duration::from_millis(2020)
1000 );
1001 assert_eq!(
1002 Duration::try_from(p.cbt_initial_timeout).unwrap(),
1003 Duration::from_millis(2050)
1004 );
1005 assert_eq!(
1006 Duration::try_from(p.cbt_testing_delay).unwrap(),
1007 Duration::from_secs(110)
1008 );
1009 assert_eq!(p.cbt_max_open_circuits_for_testing.get(), 14);
1010 assert_eq!(p.circuit_window.get(), 999);
1011 assert_eq!(
1012 Duration::try_from(p.circuit_priority_half_life).unwrap(),
1013 Duration::from_millis(222)
1014 );
1015 assert!(!bool::from(p.extend_by_ed25519_id));
1016 assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 51);
1017 assert_eq!(
1018 Duration::try_from(p.unused_client_circ_timeout).unwrap(),
1019 Duration::from_secs(606)
1020 );
1021 assert_eq!(p.sendme_accept_min_version.get(), 31);
1022 assert_eq!(p.sendme_emit_min_version.get(), 32);
1023
1024 assert_eq!(
1025 Duration::try_from(p.guard_lifetime_unconfirmed).unwrap(),
1026 Duration::from_secs(86400 * 36)
1027 );
1028 assert_eq!(
1029 Duration::try_from(p.guard_lifetime_confirmed).unwrap(),
1030 Duration::from_secs(86400 * 37)
1031 );
1032 assert_eq!(
1033 Duration::try_from(p.guard_internet_likely_down).unwrap(),
1034 Duration::from_secs(38)
1035 );
1036 assert_eq!(p.guard_max_sample_size.get(), 39);
1037 assert_eq!(p.guard_max_sample_threshold.as_percent().get(), 40);
1038 assert_eq!(p.guard_filtered_min_sample_size.get(), 41);
1039 assert_eq!(p.guard_n_primary.get(), 42);
1040 assert_eq!(p.guard_use_parallelism.get(), 43);
1041 assert_eq!(p.guard_dir_use_parallelism.get(), 44);
1042 assert_eq!(
1043 Duration::try_from(p.guard_nonprimary_connect_timeout).unwrap(),
1044 Duration::from_secs(45)
1045 );
1046 assert_eq!(
1047 Duration::try_from(p.guard_nonprimary_idle_timeout).unwrap(),
1048 Duration::from_secs(46)
1049 );
1050 assert_eq!(
1051 Duration::try_from(p.guard_remove_unlisted_after).unwrap(),
1052 Duration::from_secs(86400 * 47)
1053 );
1054 assert_eq!(p.guard_meaningful_restriction.as_percent().get(), 12);
1055 assert_eq!(p.guard_extreme_restriction.as_percent().get(), 3);
1056 }
1057}