1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
|
//! Implements a usable view of Tor network parameters.
//!
//! The Tor consensus document contains a number of 'network
//! parameters', which are integer-valued items voted on by the
//! directory authorities. They are used to tune the behavior of
//! numerous aspects of the network.
//! A set of Tor network parameters
//!
//! The Tor consensus document contains a number of 'network
//! parameters', which are integer-valued items voted on by the
//! directory authorities. These parameters are used to tune the
//! behavior of numerous aspects of the network.
//!
//! This type differs from
//! [`NetParams`](tor_netdoc::doc::netstatus::NetParams) in that it
//! only exposes a set of parameters recognized by arti. In return
//! for this restriction, it makes sure that the values it gives are
//! in range, and provides default values for any parameters that are
//! missing.
use tor_units::{BoundedInt32, IntegerMilliseconds, Percentage, SendMeVersion};
/// This structure holds recognised configuration parameters. All values are type-safe,
/// and where applicable clamped to be within range.
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct NetParameters {
/// A weighting factor for bandwidth calculations
pub bw_weight_scale: BoundedInt32<0, { i32::MAX }>,
/// The maximum cell window size?
pub circuit_window: BoundedInt32<100, 1000>,
/// The decay parameter for circuit priority
pub circuit_priority_half_life: IntegerMilliseconds<BoundedInt32<1, { i32::MAX }>>,
/// Whether to perform circuit extenstions by Ed25519 ID
pub extend_by_ed25519_id: BoundedInt32<0, 1>,
/// The minimum threshold for circuit patch construction
pub min_circuit_path_threshold: Percentage<BoundedInt32<25, 95>>,
/// The minimum sendme version to accept.
pub send_me_accept_min_version: SendMeVersion,
/// The minimum sendme version to transmit.
pub send_me_emit_min_version: SendMeVersion,
}
impl Default for NetParameters {
fn default() -> Self {
NetParameters {
bw_weight_scale: BoundedInt32::checked_new(10000).unwrap(),
circuit_window: BoundedInt32::checked_new(1000).unwrap(),
circuit_priority_half_life: IntegerMilliseconds::new(
BoundedInt32::checked_new(30000).unwrap(),
),
extend_by_ed25519_id: BoundedInt32::checked_new(0).unwrap(),
min_circuit_path_threshold: Percentage::new(BoundedInt32::checked_new(60).unwrap()),
send_me_accept_min_version: SendMeVersion::new(0),
send_me_emit_min_version: SendMeVersion::new(0),
}
}
}
impl NetParameters {
/// Replace the parameter whose name is `name` with the `value`,
/// clamping the value to be within allowable bounds.
///
/// Return true if the parameter was recognized; false otherwise.
fn saturating_update_override(&mut self, name: &str, value: i32) -> bool {
match name {
"bwweightscale" => {
self.bw_weight_scale = BoundedInt32::saturating_from(value);
}
"circwindow" => {
self.circuit_window = BoundedInt32::saturating_from(value);
}
"CircuitPriorityHalflifeMsec" => {
self.circuit_priority_half_life =
IntegerMilliseconds::new(BoundedInt32::saturating_from(value))
}
"ExtendByEd25519ID" => {
self.extend_by_ed25519_id = BoundedInt32::saturating_from(value);
}
"min_paths_for_circs_pct" => {
self.min_circuit_path_threshold =
Percentage::new(BoundedInt32::saturating_from(value));
}
"sendme_accept_min_version" => {
self.send_me_accept_min_version =
SendMeVersion::new(BoundedInt32::<0, 255>::saturating_from(value).into());
}
"sendme_emit_min_version" => {
self.send_me_emit_min_version =
SendMeVersion::new(BoundedInt32::<0, 255>::saturating_from(value).into());
}
_ => {
return false;
} // unrecognized parameters are ignored.
}
true
}
/// Replace a list of parameters, using the logic of
/// `saturating_update_override`.
///
/// Return a vector of the parameter names we didn't recognize.
pub(crate) fn saturating_update<'a>(
&mut self,
iter: impl Iterator<Item = (&'a String, &'a i32)>,
) -> Vec<&'a String> {
let mut unrecognized = Vec::new();
for (k, v) in iter {
if !self.saturating_update_override(k, *v) {
unrecognized.push(k);
}
}
unrecognized
}
}
#[cfg(test)]
#[allow(clippy::many_single_char_names)]
mod test {
use super::*;
use std::string::String;
#[test]
fn empty_list() {
let mut x = NetParameters::default();
let y = Vec::<(&String, &i32)>::new();
let u = x.saturating_update(y.into_iter());
assert!(u.is_empty());
}
#[test]
fn unknown_parameter() {
let mut x = NetParameters::default();
let mut y = Vec::<(&String, &i32)>::new();
let k = &String::from("This_is_not_a_real_key");
let v = &456;
y.push((k, v));
let u = x.saturating_update(y.into_iter());
assert_eq!(u, vec![&String::from("This_is_not_a_real_key")])
}
// #[test]
// fn duplicate_parameter() {}
#[test]
fn single_good_parameter() {
let mut x = NetParameters::default();
let mut y = Vec::<(&String, &i32)>::new();
let k = &String::from("min_paths_for_circs_pct");
let v = &54;
y.push((k, v));
let z = x.saturating_update(y.into_iter());
assert!(z.is_empty());
assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
}
#[test]
fn multiple_good_parameters() {
let mut x = NetParameters::default();
let mut y = Vec::<(&String, &i32)>::new();
let k = &String::from("min_paths_for_circs_pct");
let v = &54;
y.push((k, v));
let k = &String::from("circwindow");
let v = &900;
y.push((k, v));
let z = x.saturating_update(y.into_iter());
assert!(z.is_empty());
assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 54);
assert_eq!(x.circuit_window.get(), 900);
}
#[test]
fn good_out_of_range() {
let mut x = NetParameters::default();
let mut y = Vec::<(&String, &i32)>::new();
let k = &String::from("sendme_accept_min_version");
let v = &30;
y.push((k, v));
let k = &String::from("min_paths_for_circs_pct");
let v = &255;
y.push((k, v));
let z = x.saturating_update(y.into_iter());
assert!(z.is_empty());
assert_eq!(x.send_me_accept_min_version.get(), 30);
assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
}
#[test]
fn good_invalid_rep() {
let mut x = NetParameters::default();
let mut y = Vec::<(&String, &i32)>::new();
let k = &String::from("sendme_accept_min_version");
let v = &30;
y.push((k, v));
let k = &String::from("min_paths_for_circs_pct");
let v = &9000;
y.push((k, v));
let z = x.saturating_update(y.into_iter());
assert!(z.is_empty());
assert_eq!(x.send_me_accept_min_version.get(), 30);
assert_eq!(x.min_circuit_path_threshold.as_percent().get(), 95);
}
// #[test]
// fn good_duplicate() {}
#[test]
fn good_unknown() {
let mut x = NetParameters::default();
let mut y = Vec::<(&String, &i32)>::new();
let k = &String::from("sendme_accept_min_version");
let v = &30;
y.push((k, v));
let k = &String::from("not_a_real_parameter");
let v = &9000;
y.push((k, v));
let z = x.saturating_update(y.into_iter());
assert_eq!(z, vec![&String::from("not_a_real_parameter")]);
assert_eq!(x.send_me_accept_min_version.get(), 30);
}
#[test]
fn from_consensus() {
let mut p = NetParameters::default();
let mut mp: std::collections::HashMap<String, i32> = std::collections::HashMap::new();
mp.insert("bwweightscale".to_string(), 70);
mp.insert("min_paths_for_circs_pct".to_string(), 45);
mp.insert("im_a_little_teapot".to_string(), 1);
mp.insert("circwindow".to_string(), 99999);
mp.insert("ExtendByEd25519ID".to_string(), 1);
let z = p.saturating_update(mp.iter());
assert_eq!(z, vec![&String::from("im_a_little_teapot")]);
assert_eq!(p.bw_weight_scale.get(), 70);
assert_eq!(p.min_circuit_path_threshold.as_percent().get(), 45);
let b_val: bool = p.extend_by_ed25519_id.into();
assert_eq!(b_val, true);
}
}
|