1use crate::{LowLevelRelayPredicate, RelayExclusion, RelayRestriction, RelayUsage};
5use tor_basic_utils::iter::FilterCount;
6use tor_netdir::{NetDir, Relay, WeightRole};
7
8use std::fmt;
9
10#[derive(Clone, Debug)]
21pub struct RelaySelector<'a> {
22 usage: Restr<'a>,
26
27 exclusion: Restr<'a>,
31
32 other_restrictions: Vec<Restr<'a>>,
34}
35
36#[derive(Clone, Debug)]
38struct Restr<'a> {
39 restriction: RelayRestriction<'a>,
41 strict: bool,
43}
44
45impl<'a> Restr<'a> {
46 fn maybe_relax(&self) -> Self {
50 if self.strict {
51 self.clone()
52 } else {
53 Self {
54 restriction: self.restriction.relax(),
55 strict: true,
58 }
59 }
60 }
61}
62
63#[derive(Debug, Clone)]
72pub struct SelectionInfo<'a> {
73 first_try: FilterCounts,
75
76 relaxed_try: Option<FilterCounts>,
79
80 succeeded: bool,
82
83 in_selection: &'a RelaySelector<'a>,
87}
88
89impl<'a> SelectionInfo<'a> {
90 pub fn success(&self) -> bool {
95 self.succeeded
96 }
97
98 pub fn result_is_relaxed_success(&self) -> bool {
101 self.relaxed_try.is_some() && self.succeeded
102 }
103}
104
105impl<'a> fmt::Display for SelectionInfo<'a> {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 match (self.succeeded, &self.relaxed_try) {
108 (true, None) => write!(f, "Success: {}", FcDisp(&self.first_try, self.in_selection))?,
109 (false, None) => write!(f, "Failed: {}", FcDisp(&self.first_try, self.in_selection))?,
110 (true, Some(retry)) => write!(
111 f,
112 "Failed at first, then succeeded. At first, {}. After relaxing requirements, {}",
113 FcDisp(&self.first_try, self.in_selection),
114 FcDisp(retry, self.in_selection)
115 )?,
116 (false, Some(retry)) => write!(
117 f,
118 "Failed even after relaxing requirement. At first, {}. After relaxing requirements, {}",
119 FcDisp(&self.first_try, self.in_selection),
120 FcDisp(retry, self.in_selection)
121 )?,
122 };
123 Ok(())
124 }
125}
126
127#[derive(Debug, Clone)]
129struct FilterCounts {
130 counts: Vec<FilterCount>,
138}
139
140impl FilterCounts {
141 fn new(selector: &RelaySelector) -> Self {
143 let counts = vec![FilterCount::default(); selector.n_restrictions()];
144 FilterCounts { counts }
145 }
146}
147
148struct FcDisp<'a>(&'a FilterCounts, &'a RelaySelector<'a>);
150impl<'a> fmt::Display for FcDisp<'a> {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 let counts = &self.0.counts;
153 let restrictions = self.1.all_restrictions();
154 write!(f, "rejected ")?;
155 let mut first = true;
156 let mut found_any_rejected = false;
157 for (c, r) in counts.iter().zip(restrictions) {
158 if let Some(desc) = r.restriction.rejection_description() {
159 if first {
160 first = false;
161 } else {
162 write!(f, "; ")?;
163 }
164 write!(f, "{} as {}", c.display_frac_rejected(), desc)?;
165 found_any_rejected = true;
166 } else {
167 debug_assert_eq!(c.n_rejected, 0);
168 }
169 }
170 if !found_any_rejected {
171 write!(f, "none")?;
172 }
173 Ok(())
174 }
175}
176
177impl<'a> RelaySelector<'a> {
178 pub fn new(usage: RelayUsage, exclusion: RelayExclusion<'a>) -> Self {
188 Self {
189 usage: Restr {
190 restriction: RelayRestriction::for_usage(usage),
191 strict: true,
192 },
193 exclusion: Restr {
194 restriction: exclusion.into(),
195 strict: true,
196 },
197 other_restrictions: vec![],
198 }
199 }
200
201 pub fn mark_usage_flexible(&mut self) {
203 self.usage.strict = false;
204 }
205
206 pub fn mark_exclusion_flexible(&mut self) {
208 self.exclusion.strict = false;
209 }
210
211 pub fn push_restriction(&mut self, restriction: RelayRestriction<'a>) {
213 self.push_inner(restriction, true);
214 }
215
216 pub fn push_flexible_restriction(&mut self, restriction: RelayRestriction<'a>) {
218 self.push_inner(restriction, false);
219 }
220
221 fn push_inner(&mut self, restriction: RelayRestriction<'a>, strict: bool) {
223 self.other_restrictions.push(Restr {
224 restriction,
225 strict,
226 });
227 }
228
229 pub fn usage(&self) -> &RelayUsage {
231 self.usage
233 .restriction
234 .as_usage()
235 .expect("Usage not a usage!?")
236 }
237
238 fn weight_role(&self) -> WeightRole {
241 self.usage().selection_weight_role()
242 }
243
244 pub fn permits_relay(&self, relay: &tor_netdir::Relay<'_>) -> bool {
246 self.low_level_predicate_permits_relay(relay)
247 }
248
249 fn all_restrictions(&self) -> impl Iterator<Item = &Restr<'a>> {
252 use std::iter::once;
253 once(&self.usage)
254 .chain(once(&self.exclusion))
255 .chain(self.other_restrictions.iter())
256 }
257
258 fn n_restrictions(&self) -> usize {
261 self.other_restrictions.len() + 2
262 }
263
264 pub fn select_relay<'s, 'd, R: rand::Rng>(
267 &'s self,
268 rng: &mut R,
269 netdir: &'d NetDir,
270 ) -> (Option<Relay<'d>>, SelectionInfo<'s>) {
271 with_possible_relaxation(
272 self,
273 |selector| {
274 let role = selector.weight_role();
275 let mut fc = FilterCounts::new(selector);
276 let relay = netdir.pick_relay(rng, role, |r| selector.relay_usable(r, &mut fc));
277 (relay, fc)
278 },
279 Option::is_some,
280 )
281 }
282
283 pub fn select_n_relays<'s, 'd, R: rand::Rng>(
286 &'s self,
287 rng: &mut R,
288 n_relays: usize,
289 netdir: &'d NetDir,
290 ) -> (Vec<Relay<'d>>, SelectionInfo<'s>) {
291 with_possible_relaxation(
292 self,
293 |selector| {
294 let role = selector.weight_role();
295 let mut fc = FilterCounts::new(selector);
296 let relays = netdir
297 .pick_n_relays(rng, n_relays, role, |r| selector.relay_usable(r, &mut fc));
298 (relays, fc)
299 },
300 |relays| !relays.is_empty(),
301 )
302 }
303
304 fn relay_usable(&self, r: &Relay<'_>, fc: &mut FilterCounts) -> bool {
313 debug_assert_eq!(self.n_restrictions(), fc.counts.len());
314
315 self.all_restrictions()
316 .zip(fc.counts.iter_mut())
317 .all(|(restr, restr_count)| {
318 restr_count.count(restr.restriction.low_level_predicate_permits_relay(r))
319 })
320 }
321
322 fn can_relax(&self) -> bool {
324 self.all_restrictions().any(|restr| !restr.strict)
325 }
326
327 fn relax(&self) -> Self {
330 let new_selector = RelaySelector {
331 usage: self.usage.maybe_relax(),
332 exclusion: self.exclusion.maybe_relax(),
333 other_restrictions: self
334 .other_restrictions
335 .iter()
336 .map(Restr::maybe_relax)
337 .collect(),
338 };
339 debug_assert!(!new_selector.can_relax());
340 new_selector
341 }
342}
343
344impl<'a> LowLevelRelayPredicate for RelaySelector<'a> {
345 fn low_level_predicate_permits_relay(&self, relay: &tor_netdir::Relay<'_>) -> bool {
346 self.all_restrictions()
347 .all(|r| r.restriction.low_level_predicate_permits_relay(relay))
348 }
349}
350
351fn with_possible_relaxation<'a, SEL, OK, T>(
366 selector: &'a RelaySelector,
367 mut select: SEL,
368 ok: OK,
369) -> (T, SelectionInfo<'a>)
370where
371 SEL: FnMut(&RelaySelector) -> (T, FilterCounts),
372 OK: Fn(&T) -> bool,
373{
374 let (outcome, count_strict) = select(selector);
375 let succeeded = ok(&outcome);
376 if succeeded || !selector.can_relax() {
377 let info = SelectionInfo {
378 first_try: count_strict,
379 relaxed_try: None,
380 succeeded,
381 in_selection: selector,
382 };
383 return (outcome, info);
384 }
385 let relaxed_selector = selector.relax();
386 let (relaxed_outcome, count_relaxed) = select(&relaxed_selector);
387 let info = SelectionInfo {
388 first_try: count_strict,
389 relaxed_try: Some(count_relaxed),
390 succeeded: ok(&relaxed_outcome),
391 in_selection: selector,
392 };
393 (relaxed_outcome, info)
394}
395
396#[cfg(test)]
397mod test {
398 #![allow(clippy::bool_assert_comparison)]
400 #![allow(clippy::clone_on_copy)]
401 #![allow(clippy::dbg_macro)]
402 #![allow(clippy::mixed_attributes_style)]
403 #![allow(clippy::print_stderr)]
404 #![allow(clippy::print_stdout)]
405 #![allow(clippy::single_char_pattern)]
406 #![allow(clippy::unwrap_used)]
407 #![allow(clippy::unchecked_duration_subtraction)]
408 #![allow(clippy::useless_vec)]
409 #![allow(clippy::needless_pass_by_value)]
410 use std::collections::HashSet;
413
414 use tor_basic_utils::test_rng::testing_rng;
415 use tor_linkspec::{HasRelayIds, RelayId};
416 use tor_netdir::{FamilyRules, Relay, SubnetConfig};
417
418 use super::*;
419 use crate::{
420 testing::{cfg, split_netdir, testnet},
421 RelaySelectionConfig, TargetPort,
422 };
423
424 #[test]
425 fn selector_as_predicate() {
426 let nd = testnet();
427 let id_4 = "$0404040404040404040404040404040404040404".parse().unwrap();
428 let usage = RelayUsage::middle_relay(None);
429 let exclusion = RelayExclusion::exclude_identities([id_4].into_iter().collect());
430 let sel = RelaySelector::new(usage.clone(), exclusion.clone());
431
432 let (yes, no) = split_netdir(&nd, &sel);
433 let p = |r: &Relay<'_>| {
434 usage.low_level_predicate_permits_relay(r)
435 && exclusion.low_level_predicate_permits_relay(r)
436 };
437 assert!(yes.iter().all(p));
438 assert!(no.iter().all(|r| !p(r)));
439 }
440
441 #[test]
442 fn selector_as_filter() {
443 let nd = testnet();
444 let id_4 = "$0404040404040404040404040404040404040404".parse().unwrap();
445 let usage = RelayUsage::middle_relay(None);
446 let exclusion = RelayExclusion::exclude_identities([id_4].into_iter().collect());
447 let sel = RelaySelector::new(usage.clone(), exclusion.clone());
448 let mut fc = FilterCounts::new(&sel);
449
450 let (yes, _no) = split_netdir(&nd, &sel);
451 let filtered: Vec<_> = nd
452 .relays()
453 .filter(|r| sel.relay_usable(r, &mut fc))
454 .collect();
455 assert_eq!(yes.len(), filtered.len());
456
457 let k1: HashSet<_> = yes.iter().map(|r| r.rsa_identity().unwrap()).collect();
458 let k2: HashSet<_> = filtered.iter().map(|r| r.rsa_identity().unwrap()).collect();
459 assert_eq!(k1, k2);
460
461 assert_eq!(fc.counts[0].n_rejected, 12);
464 assert_eq!(fc.counts[1].n_rejected, 1);
466 assert_eq!(fc.counts[1].n_accepted, yes.len());
468 }
469
470 #[test]
471 fn selector_pick_random() {
472 let nd = testnet();
473 let id_4 = "$0404040404040404040404040404040404040404".parse().unwrap();
474 let usage = RelayUsage::middle_relay(None);
475 let exclusion = RelayExclusion::exclude_identities([id_4].into_iter().collect());
476 let sel = RelaySelector::new(usage.clone(), exclusion.clone());
477
478 let (yes, _no) = split_netdir(&nd, &sel);
479 let k_yes: HashSet<_> = yes.iter().map(|r| r.rsa_identity().unwrap()).collect();
480 let p = |r: Relay<'_>| k_yes.contains(r.rsa_identity().unwrap());
481
482 let mut rng = testing_rng();
483 for _ in 0..50 {
484 let (r_rand, si) = sel.select_relay(&mut rng, &nd);
486 assert!(si.success());
487 assert!(!si.result_is_relaxed_success());
488 assert!(p(r_rand.unwrap()));
489
490 let (rs_rand, si) = sel.select_n_relays(&mut rng, 20, &nd);
492 assert_eq!(rs_rand.len(), 20);
493 assert!(si.success());
494 assert!(!si.result_is_relaxed_success());
495 assert!(rs_rand.iter().cloned().all(p));
496 let k_got: HashSet<_> = rs_rand.iter().map(|r| r.rsa_identity().unwrap()).collect();
497 assert_eq!(k_got.len(), 20);
498 }
499 }
500
501 #[test]
502 fn selector_report() {
503 let nd = testnet();
504 let id_4 = "$0404040404040404040404040404040404040404".parse().unwrap();
505 let usage = RelayUsage::middle_relay(None);
506 let exclusion = RelayExclusion::exclude_identities([id_4].into_iter().collect());
507 let sel = RelaySelector::new(usage.clone(), exclusion.clone());
508
509 let mut rng = testing_rng();
510 let (_, si) = sel.select_relay(&mut rng, &nd);
511 assert_eq!(
512 si.to_string(),
513 "Success: rejected 12/40 as useless for middle relay; 1/28 as already selected"
514 );
515
516 let unreachable_port = TargetPort::ipv6(80);
519 let sel = RelaySelector::new(
520 RelayUsage::exit_to_all_ports(&cfg(), vec![unreachable_port]),
521 exclusion.clone(),
522 );
523 let (r_none, si) = sel.select_relay(&mut rng, &nd);
524 assert!(r_none.is_none());
525 assert_eq!(
526 si.to_string(),
527 "Failed: rejected 40/40 as not exiting to desired ports; 0/0 as already selected"
528 );
529 }
530
531 #[test]
532 fn relax() {
533 let all_families = FamilyRules::all_family_info();
534
535 let nd = testnet();
536 let id_4: RelayId = "$0404040404040404040404040404040404040404".parse().unwrap();
537 let r4 = nd.by_id(&id_4).unwrap();
538 let usage = RelayUsage::middle_relay(None);
539 let very_silly_cfg = RelaySelectionConfig {
540 long_lived_ports: cfg().long_lived_ports,
541 subnet_config: SubnetConfig::new(1, 1),
543 };
544 let exclude_relays = vec![r4];
545 let exclude_everyone = RelayExclusion::exclude_relays_in_same_family(
546 &very_silly_cfg,
547 exclude_relays,
548 all_families,
549 );
550
551 let mut sel = RelaySelector::new(usage.clone(), exclude_everyone.clone());
552 let mut rng = testing_rng();
553 let (r_none, _) = sel.select_relay(&mut rng, &nd);
554 assert!(r_none.is_none());
555
556 sel.mark_exclusion_flexible();
557 let (r_some, si) = sel.select_relay(&mut rng, &nd);
558 assert!(r_some.is_some());
559 assert_eq!(si.to_string(), "Failed at first, then succeeded. At first, rejected 12/40 as useless for middle relay; \
560 28/28 as in same family as already selected. \
561 After relaxing requirements, rejected 12/40 as useless for middle relay; \
562 0/28 as in same family as already selected");
563 }
564}