1
//! Logic for manipulating a sampled set of guards, along with various
2
//! orderings on that sample.
3

            
4
mod candidate;
5

            
6
use crate::filter::GuardFilter;
7
use crate::guard::{Guard, NewlyConfirmed, Reachable};
8
use crate::skew::SkewObservation;
9
use crate::{
10
    ids::GuardId, ExternalActivity, GuardParams, GuardUsage, GuardUsageKind, PickGuardError,
11
};
12
use crate::{FirstHop, GuardSetSelector};
13
use tor_basic_utils::iter::{FilterCount, IteratorExt as _};
14
use tor_linkspec::{ByRelayIds, HasRelayIds};
15

            
16
use itertools::Itertools;
17
use rand::seq::IndexedRandom;
18
use serde::{Deserialize, Serialize};
19
use std::borrow::Cow;
20
use std::collections::{HashMap, HashSet};
21
use std::time::{Instant, SystemTime};
22
use tracing::{debug, info};
23

            
24
#[allow(unused_imports)]
25
pub(crate) use candidate::{Candidate, CandidateStatus, Universe, UniverseRef, WeightThreshold};
26

            
27
/// A set of sampled guards, along with various orderings on subsets
28
/// of the sample.
29
///
30
/// Every guard in a `GuardSet` is considered to be "sampled": that
31
/// is, selected from a network directory at some point in the past.
32
/// The guards in the sample are ordered (roughly) by the time at
33
/// which they were added.  This list is persistent.
34
///
35
/// Any guard which we've successfully used at least once is
36
/// considered "confirmed".  Confirmed guards are ordered (roughly) by
37
/// the time at which we first used them.  This list is persistent.
38
///
39
/// The guards which we would prefer to use are called "primary".
40
/// Primary guards are ordered from most- to least-preferred.
41
/// This list is not persistent, and is re-derived as needed.
42
///
43
/// These lists together define a "preference order".  All primary
44
/// guards come first in preference order.  Then come the non-primary
45
/// confirmed guards, in their confirmed order.  Finally come the
46
/// non-primary, non-confirmed guards, in their sampled order.
47
#[derive(Debug, Default, Clone, Deserialize)]
48
#[serde(from = "GuardSample")]
49
pub(crate) struct GuardSet {
50
    /// Map from identities to guards, for every guard in this sample.
51
    ///
52
    /// The key for each entry is a set of identities which we have
53
    /// good (trustworthy-enough) reason to link together.
54
    ///
55
    /// When we connect to a guard we require it to demonstrate
56
    /// that it has *all* of these identities;
57
    /// and we do pinning, so that we note down the other identities we discover it has,
58
    /// with the intent that we will require them in future.
59
    ///
60
    /// ### Sources of linkage:
61
    ///
62
    ///  * If we connect to a relay and it proves a set of identities,
63
    ///    that necessarily will include at least the ones we have already.
64
    ///    We can add any other identities we have discovered.
65
    ///    Justification: the owners of the old ids have made a statement
66
    ///    (via the connection protocols) that these other ids are also theirs,
67
    ///    and should be required in future.
68
    ///
69
    ///  * If we obtain a (full) descriptor for a relay, and check the
70
    ///    self-signatures by all the identities we have already,
71
    ///    we can add any other identities listed in the descriptor.
72
    ///    Justification: the owners of the old ids have made an explicit statement
73
    ///    that these other ids are also theirs,
74
    ///    and should be required in future.
75
    ///
76
    ///  * For a relay in the netdir, if the netdir links some ids together,
77
    ///    we can combine the entries.
78
    ///    Justification: the netdir is authoritative for netdir-based relays.
79
    ///
80
    ///  * For a configured bridge, if our configuration links some identities,
81
    ///    we must insist on all those identities.
82
    ///    So we combine them.
83
    ///
84
    /// ### Handling of conflicting entries:
85
    ///
86
    /// `ByRelayIds` will implicitly delete conflicting entries,
87
    /// simply forgetting about them.
88
    /// This is OK for netdir relays, since we do not expect this to occur in practice.
89
    ///
90
    /// For bridges, conflicts may in fact occur,
91
    /// since bridge lines are not issued by a single authority,
92
    /// and should be afforded limited trust.
93
    ///
94
    ///  * If the configuration contains bridge lines that mutually conflict,
95
    ///    affected bridge lines should be disregarded,
96
    ///    or the configuration rejected.
97
    ///
98
    ///  * If the configuration contains information which is inconsistent with
99
    ///    our past experience, we should discard the past experiences which
100
    ///    aren't reconcilable with the configuration.
101
    ///
102
    ///  * We may discover a linkage which demonstrates that the configuration
103
    ///    is wrong: for example, two bridge lines for identities X and Y,
104
    ///    but in fact there is only one bridge with both identities.
105
    ///    In this situation it is OK to effectively disregard some the configuration
106
    ///    entries which are at variance with reality, maybe with a warning,
107
    ///    but keeping at least one of every usable id set (actually existing bridge)
108
    ///    would be good.
109
    guards: ByRelayIds<Guard>,
110
    /// Identities of all the guards in the sample, in sample order.
111
    ///
112
    /// This contains the same elements as the keys of `guards`
113
    sample: Vec<GuardId>,
114
    /// Identities of all the confirmed guards in the sample, in
115
    /// confirmed order.
116
    ///
117
    /// This contains a subset of the values in `sample`.
118
    confirmed: Vec<GuardId>,
119
    /// Identities of all the primary guards, in preference order
120
    /// (from best to worst).
121
    ///
122
    /// This contains a subset of the values in `sample`.
123
    primary: Vec<GuardId>,
124
    /// Currently active filter that restricts which guards we can use.
125
    ///
126
    /// Note that all of the lists above (with the exception of `primary`)
127
    /// can hold guards that the filter doesn't permit.  This behavior
128
    /// is meant to give good security behavior in the presence of filters
129
    /// that change over time.
130
    active_filter: GuardFilter,
131

            
132
    /// If true, the active filter is "very restrictive".
133
    filter_is_restrictive: bool,
134

            
135
    /// Set to 'true' whenever something changes that would force us
136
    /// to call 'select_primary_guards()', and cleared whenever we call it.
137
    primary_guards_invalidated: bool,
138

            
139
    /// Fields from the state file that was used to make this `GuardSet` that
140
    /// this version of Arti doesn't understand.
141
    unknown_fields: HashMap<String, JsonValue>,
142
}
143

            
144
/// Which of our lists did a given guard come from?
145
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
146
pub(crate) enum ListKind {
147
    /// A guard that came from the primary guard list.
148
    Primary,
149
    /// A non-primary guard that came from the confirmed guard list.
150
    Confirmed,
151
    /// A non-primary, non-confirmed guard.
152
    Sample,
153
    /// Not a guard at all, but a fallback directory.
154
    Fallback,
155
}
156

            
157
impl ListKind {
158
    /// Return true if this is a primary guard.
159
711006
    pub(crate) fn is_primary(&self) -> bool {
160
711006
        self == &ListKind::Primary
161
711006
    }
162

            
163
    /// Return true if this guard's origin indicates that you can use successful
164
    /// circuits built through it immediately without waiting for any other
165
    /// circuits to succeed or fail.
166
348506
    pub(crate) fn usable_immediately(&self) -> bool {
167
348506
        match self {
168
348490
            ListKind::Primary | ListKind::Fallback => true,
169
16
            ListKind::Confirmed | ListKind::Sample => false,
170
        }
171
348506
    }
172
}
173

            
174
impl GuardSet {
175
    /// Return the lengths of the different elements of the guard set.
176
    ///
177
    /// Used to report bugs or corruption in consistency.
178
906706
    fn inner_lengths(&self) -> (usize, usize, usize, usize) {
179
906706
        (
180
906706
            self.guards.len(),
181
906706
            self.sample.len(),
182
906706
            self.confirmed.len(),
183
906706
            self.primary.len(),
184
906706
        )
185
906706
    }
186

            
187
    /// Remove all elements from this `GuardSet` that ought to be referenced by
188
    /// another element, but which are not.
189
    ///
190
    /// This method only removes corrupted elements and updates IDs in the ID
191
    /// list (possibly adding new IDs); it doesn't add guards or other data.
192
    /// It won't do anything if the `GuardSet` is well-formed.
193
463971
    fn fix_consistency(&mut self) {
194
        /// Remove every element of `id_list` that does not belong to some guard
195
        /// in `guards`, and update the others to have any extra identities
196
        /// listed in `guards`.
197
1391913
        fn fix_id_list(guards: &ByRelayIds<Guard>, id_list: &mut Vec<GuardId>) {
198
5228466
            id_list.retain_mut(|id| match guards.by_all_ids(id) {
199
5193384
                Some(guard) => {
200
5193384
                    *id = guard.guard_id().clone();
201
5193384
                    true
202
                }
203
                None => false,
204
5228466
            });
205
1391913
        }
206

            
207
463971
        let sample_set: HashSet<_> = self.sample.iter().collect();
208
4027718
        self.guards.retain(|g| sample_set.contains(g.guard_id()));
209
463971
        fix_id_list(&self.guards, &mut self.sample);
210
463971
        fix_id_list(&self.guards, &mut self.confirmed);
211
463971
        fix_id_list(&self.guards, &mut self.primary);
212
463971
    }
213

            
214
    /// Assert that this `GuardSet` is internally consistent.
215
    ///
216
    /// Incidentally fixes the consistency of this `GuardSet` if needed.
217
453327
    fn assert_consistency(&mut self) {
218
453327
        let len_pre = self.inner_lengths();
219
453327
        self.fix_consistency();
220
453327
        let len_post = self.inner_lengths();
221
453327
        assert_eq!(len_pre, len_post);
222
453327
    }
223

            
224
    /// Return the guard that has every identity in `id`, if any.
225
348510
    pub(crate) fn get(&self, id: &GuardId) -> Option<&Guard> {
226
348510
        self.guards.by_all_ids(id)
227
348510
    }
228

            
229
    /// Replace the filter used by this `GuardSet` with `filter`.
230
    ///
231
    /// Removes all primary guards that the filter doesn't permit.
232
    ///
233
    /// If `restrictive` is true, this filter is treated as "extremely restrictive".
234
672
    pub(crate) fn set_filter(&mut self, filter: GuardFilter, restrictive: bool) {
235
672
        self.active_filter = filter;
236
672
        self.filter_is_restrictive = restrictive;
237
672

            
238
672
        self.assert_consistency();
239
672

            
240
672
        let guards = &self.guards; // avoid borrow issues
241
672
        let filt = &self.active_filter;
242
672
        self.primary.retain(|id| {
243
            guards
244
                .by_all_ids(id)
245
                .map(|g| g.usable() && filt.permits(g))
246
                .unwrap_or(false)
247
672
        });
248
672

            
249
672
        self.primary_guards_invalidated = true;
250
672
    }
251

            
252
    /// Return the current filter for this `GuardSet`.
253
8247
    pub(crate) fn filter(&self) -> &GuardFilter {
254
8247
        &self.active_filter
255
8247
    }
256

            
257
    /// Copy non-persistent status from every guard shared with `other`.
258
    ///
259
    /// This is used as part of our reload process when we don't own our state
260
    /// files, and we're reloading in order to find out what the other Arti
261
    /// instance thinks the guards are. At that point, `self` is the set of
262
    /// guards that we just loaded from state, and `other` is our old guards,
263
    /// which we are using only for their status information.
264
4
    pub(crate) fn copy_ephemeral_status_into_newly_loaded_state(&mut self, mut other: GuardSet) {
265
4
        let old_guards = std::mem::take(&mut self.guards);
266
4
        self.guards = old_guards
267
4
            .into_values()
268
22
            .map(|guard| {
269
20
                let id = guard.guard_id();
270

            
271
20
                if let Some(other_guard) = other.guards.remove_exact(id) {
272
18
                    guard.copy_ephemeral_status_into_newly_loaded_state(other_guard)
273
                } else {
274
2
                    guard
275
                }
276
22
            })
277
4
            .collect();
278
4
    }
279

            
280
    /// Return a serializable state object that can be stored to disk
281
    /// to capture the current state of this GuardSet.
282
764
    fn get_state(&self) -> GuardSample<'_> {
283
764
        let guards = self
284
764
            .sample
285
764
            .iter()
286
801
            .map(|id| Cow::Borrowed(self.guards.by_all_ids(id).expect("Inconsistent state")))
287
764
            .collect();
288
764

            
289
764
        GuardSample {
290
764
            guards,
291
764
            confirmed: Cow::Borrowed(&self.confirmed),
292
764
            remaining: self.unknown_fields.clone(),
293
764
        }
294
764
    }
295

            
296
    /// Reconstruct a guard state from its serialized representation.
297
26
    fn from_state(state: GuardSample<'_>) -> Self {
298
26
        let mut guards = ByRelayIds::new();
299
26
        let mut sample = Vec::new();
300
76
        for guard in state.guards {
301
50
            sample.push(guard.guard_id().clone());
302
50
            guards.insert(guard.into_owned());
303
50
        }
304
26
        let confirmed = state.confirmed.into_owned();
305
26
        let primary = Vec::new();
306
26
        let mut guard_set = GuardSet {
307
26
            guards,
308
26
            sample,
309
26
            confirmed,
310
26
            primary,
311
26
            active_filter: GuardFilter::default(),
312
26
            filter_is_restrictive: false,
313
26
            primary_guards_invalidated: true,
314
26
            unknown_fields: state.remaining,
315
26
        };
316
26

            
317
26
        // Fix any inconsistencies in the stored representation.
318
26
        let len_pre = guard_set.inner_lengths();
319
26
        guard_set.fix_consistency();
320
26
        let len_post = guard_set.inner_lengths();
321
26
        if len_pre != len_post {
322
            info!(
323
                "Resolved a consistency issue in stored guard state. Diagnostic codes: {:?}, {:?}",
324
                len_pre, len_post
325
            );
326
26
        }
327
26
        debug!(
328
            n_guards = len_post.0,
329
            n_confirmed = len_post.2,
330
            "Guard set loaded."
331
        );
332

            
333
26
        guard_set
334
26
    }
335

            
336
    /// Return `Ok(true)` if `id` is definitely a member of this set, and
337
    /// `Ok(false)` if it is definitely not a member.
338
    ///
339
    /// If we cannot tell, it's because there is a guard in this sample that has
340
    /// a _subset_ of the IDs in `id`. In that case, we return
341
    /// `Err(guard_ident)`, where `guard_ident`  is the identity of that guard.
342
48
    pub(crate) fn contains(&self, id: &GuardId) -> Result<bool, &GuardId> {
343
48
        let overlapping = self.guards.all_overlapping(id);
344
48
        match &overlapping[..] {
345
16
            [singleton] => {
346
16
                if singleton.has_all_relay_ids_from(id) {
347
16
                    Ok(true)
348
                } else {
349
                    Err(singleton.guard_id())
350
                }
351
            }
352
32
            _ => Ok(false),
353
        }
354
48
    }
355

            
356
    /// If there are not enough filter-permitted usable guards in this
357
    /// sample (according to the current active filter), then add
358
    /// more, up to the limits allowed by the parameters.
359
    ///
360
    /// This is the only function that adds new guards to the sample.
361
    ///
362
    /// Guards always start out un-confirmed.
363
    ///
364
    /// Return true if any guards were added.
365
10650
    pub(crate) fn extend_sample_as_needed<U: Universe>(
366
10650
        &mut self,
367
10650
        now: SystemTime,
368
10650
        params: &GuardParams,
369
10650
        dir: &U,
370
10650
    ) -> crate::ExtendedStatus {
371
10650
        let mut any_added = crate::ExtendedStatus::No;
372
21286
        while self.extend_sample_inner(now, params, dir) {
373
10636
            any_added = crate::ExtendedStatus::Yes;
374
10636
        }
375
10650
        any_added
376
10650
    }
377

            
378
    /// Implementation helper for extend_sample_as_needed.
379
    ///
380
    /// # Complications
381
    ///
382
    /// For spec conformance, we only consider our filter when selecting new
383
    /// guards if the filter is "very restrictive". That makes it possible that
384
    /// this function will add fewer filter-permitted guards than we had wanted.
385
    /// Because of that, this is a separate function, and
386
    /// extend_sample_as_needed runs it in a loop until it returns false.
387
21286
    fn extend_sample_inner<U: Universe>(
388
21286
        &mut self,
389
21286
        now: SystemTime,
390
21286
        params: &GuardParams,
391
21286
        dir: &U,
392
21286
    ) -> bool {
393
21286
        self.assert_consistency();
394
21286
        let n_filtered_usable = self
395
21286
            .guards
396
21286
            .values()
397
103604
            .filter(|g| {
398
103604
                g.usable()
399
103604
                    && self.active_filter.permits(*g)
400
103604
                    && g.reachable() != Reachable::Unreachable
401
103604
            })
402
21286
            .count();
403
21286
        if n_filtered_usable >= params.min_filtered_sample_size {
404
431
            return false; // We have enough usage guards in our sample.
405
20855
        }
406
20855
        if self.guards.len() >= params.max_sample_size {
407
            return false; // We can't add any more guards to our sample.
408
20855
        }
409
20855

            
410
20855
        // What are the most guards we're willing to have in the sample?
411
20855
        let max_to_add = params.max_sample_size - self.sample.len();
412
20855
        let want_to_add = params.min_filtered_sample_size - n_filtered_usable;
413
20855
        let n_to_add = std::cmp::min(max_to_add, want_to_add);
414
20855

            
415
20855
        let WeightThreshold {
416
20855
            mut current_weight,
417
20855
            maximum_weight,
418
20855
        } = dir.weight_threshold(&self.guards, params);
419
20855

            
420
20855
        // Ask the netdir for a set of guards we could use.
421
20855
        let no_filter = GuardFilter::unfiltered();
422
20855
        let (n_candidates, pre_filter) =
423
20855
            if self.filter_is_restrictive || self.active_filter.is_unfiltered() {
424
20855
                (n_to_add, &self.active_filter)
425
            } else {
426
                // The filter will probably reject a bunch of guards, but we sample
427
                // before filtering, so we make this larger on an ad-hoc basis.
428
                (n_to_add * 3, &no_filter)
429
            };
430

            
431
20855
        let candidates = dir.sample(&self.guards, pre_filter, n_candidates);
432
20855

            
433
20855
        // Add those candidates to the sample.
434
20855
        let mut any_added = false;
435
20855
        let mut n_filtered_usable = n_filtered_usable;
436
124369
        for (candidate, weight) in candidates {
437
            // Don't add any more if we have met the minimal sample size, and we
438
            // have added too much weight.
439
103514
            if current_weight >= maximum_weight
440
81729
                && self.guards.len() >= params.min_filtered_sample_size
441
            {
442
                break;
443
103514
            }
444
103514
            if self.guards.len() >= params.max_sample_size {
445
                // Can't add any more.
446
                break;
447
103514
            }
448
103514
            if n_filtered_usable >= params.min_filtered_sample_size {
449
                // We've reached our target; no need to add more.
450
                break;
451
103514
            }
452
103514
            if self.active_filter.permits(&candidate.owned_target) {
453
103514
                n_filtered_usable += 1;
454
103514
            }
455
103514
            current_weight += weight;
456
103514
            self.add_guard(candidate, now, params);
457
103514
            any_added = true;
458
        }
459
20855
        self.assert_consistency();
460
20855
        any_added
461
21286
    }
462

            
463
    /// Add `relay` as a new guard.
464
    ///
465
    /// Does nothing if it is already a guard.
466
103514
    fn add_guard(&mut self, relay: Candidate, now: SystemTime, params: &GuardParams) {
467
103514
        let id = GuardId::from_relay_ids(&relay.owned_target);
468
103514
        if self.guards.by_all_ids(&id).is_some() {
469
            return;
470
103514
        }
471
103514
        debug!(guard_id=?id, "Adding guard to sample.");
472
103514
        let guard = Guard::from_candidate(relay, now, params);
473
103514
        self.guards.insert(guard);
474
103514
        self.sample.push(id);
475
103514
        self.primary_guards_invalidated = true;
476
103514
    }
477

            
478
    /// Return the number of our primary guards that are missing directory
479
    /// information in `universe`.
480
    ///
481
    /// Note that "missing directory information" is not the same as "absent":
482
    /// in this case, we  are counting the primary guards where we cannot tell
483
    /// whether they appear in the universe or not because we have not yet
484
    /// downloaded their descriptors.
485
10622
    pub(crate) fn n_primary_without_id_info_in<U: Universe>(&mut self, universe: &U) -> usize {
486
10622
        self.primary
487
10622
            .iter()
488
10622
            .filter(|id| {
489
32
                let g = self
490
32
                    .guards
491
32
                    .by_all_ids(*id)
492
32
                    .expect("Inconsistent guard state");
493
32
                g.listed_in(universe).is_none()
494
10622
            })
495
10622
            .count()
496
10622
    }
497

            
498
    /// Update the status of every guard  in this sample from a given source.
499
10618
    pub(crate) fn update_status_from_dir<U: Universe>(&mut self, dir: &U) {
500
10618
        let old_guards = std::mem::take(&mut self.guards);
501
10618
        self.guards = old_guards
502
10618
            .into_values()
503
10618
            .map(|mut guard| {
504
40
                guard.update_from_universe(dir);
505
40
                guard
506
10618
            })
507
10618
            .collect();
508
10618
        // Call "fix consistency", in case any guards got a new ID.
509
10618
        self.fix_consistency();
510
10618
    }
511

            
512
    /// Re-build the list of primary guards.
513
    ///
514
    /// Primary guards are chosen according to preference order over all
515
    /// the guards in the set, restricted by the current filter.
516
    ///
517
    /// TODO: Enumerate all the times when this function needs to be called.
518
    ///
519
    /// TODO: Make sure this is called enough.
520
355976
    pub(crate) fn select_primary_guards(&mut self, params: &GuardParams) {
521
355976
        // TODO-SPEC: This is not 100% what the spec says, but it does match what
522
355976
        // Tor does.  We pick first from the confirmed guards,
523
355976
        // then from any previous primary guards, and then from maybe-reachable
524
355976
        // guards in the sample.
525
355976

            
526
355976
        // Only for logging.
527
355976
        let old_primary = self.primary.clone();
528
355976

            
529
355976
        self.primary = self
530
355976
            // First, we look at the confirmed guards.
531
355976
            .confirmed
532
355976
            .iter()
533
355976
            // Then we consider existing primary guards.
534
355976
            .chain(self.primary.iter())
535
355976
            // Finally, we look at the rest of the sample for guards not marked
536
355976
            // as "unreachable".
537
355976
            .chain(self.reachable_sample_ids())
538
355976
            // We only consider each guard the first time it appears.
539
355976
            .unique()
540
355976
            // We only consider usable guards that the filter allows.
541
1058126
            .filter_map(|id| {
542
1049359
                let g = self
543
1049359
                    .guards
544
1049359
                    .by_all_ids(id)
545
1049359
                    .expect("Inconsistent guard state");
546
1049359
                if g.usable() && self.active_filter.permits(g) {
547
1049359
                    Some(id.clone())
548
                } else {
549
                    None
550
                }
551
1058126
            })
552
355976
            // The first n_primary guards on that list are primary!
553
355976
            .take(params.n_primary)
554
355976
            .collect();
555
355976

            
556
355976
        if self.primary != old_primary {
557
10939
            debug!(old=?old_primary, new=?self.primary, "Updated primary guards.");
558
345037
        }
559

            
560
        // Clear exploratory_circ_pending for all primary guards.
561
1405335
        for id in &self.primary {
562
1075092
            self.guards.modify_by_all_ids(id, |guard| {
563
1049359
                guard.note_exploratory_circ(false);
564
1075092
            });
565
1049359
        }
566

            
567
        // TODO: Recalculate retry times, perhaps, since we may have changed
568
        // the timeouts?
569

            
570
355976
        self.assert_consistency();
571
355976
        self.primary_guards_invalidated = false;
572
355976
    }
573

            
574
    /// Remove all guards which should expire `now`, according to the settings
575
    /// in `params`.
576
17683
    pub(crate) fn expire_old_guards(&mut self, params: &GuardParams, now: SystemTime) {
577
17683
        self.assert_consistency();
578
17683
        let n_pre = self.guards.len();
579
18064
        self.guards.retain(|g| !g.is_expired(params, now));
580
17683
        let guards = &self.guards;
581
18064
        self.sample.retain(|id| guards.by_all_ids(id).is_some());
582
17693
        self.confirmed.retain(|id| guards.by_all_ids(id).is_some());
583
17782
        self.primary.retain(|id| guards.by_all_ids(id).is_some());
584
17683
        self.assert_consistency();
585
17683

            
586
17683
        if self.guards.len() < n_pre {
587
4
            let n_expired = n_pre - self.guards.len();
588
4
            debug!(n_expired, "Expired guards as too old.");
589
4
            self.primary_guards_invalidated = true;
590
17679
        }
591
17683
    }
592

            
593
    /// Return an iterator over the Id for every Guard in the sample that
594
    /// is not known to be Unreachable.
595
355976
    fn reachable_sample_ids(&self) -> impl Iterator<Item = &GuardId> {
596
356788
        self.sample.iter().filter(move |id| {
597
30991
            let g = self
598
30991
                .guards
599
30991
                .by_all_ids(*id)
600
30991
                .expect("Inconsistent guard state");
601
30991
            g.reachable() != Reachable::Unreachable
602
356788
        })
603
355976
    }
604

            
605
    /// Return an iterator that yields an element for every guard in
606
    /// this set, in preference order.
607
    ///
608
    /// Each element contains a `ListKind` that describes which list the
609
    /// guard was in, and a `&GuardId` that identifies the guard.
610
    ///
611
    /// Note that this function will return guards that are not
612
    /// accepted by the current active filter: the caller must apply
613
    /// that filter if appropriate.
614
358112
    fn preference_order_ids(&self) -> impl Iterator<Item = (ListKind, &GuardId)> {
615
358112
        self.primary
616
358112
            .iter()
617
371674
            .map(|id| (ListKind::Primary, id))
618
358244
            .chain(self.confirmed.iter().map(|id| (ListKind::Confirmed, id)))
619
358736
            .chain(self.sample.iter().map(|id| (ListKind::Sample, id)))
620
372694
            .unique_by(|(_, id)| *id)
621
358112
    }
622

            
623
    /// Like `preference_order_ids`, but yields `&Guard` instead of `&GuardId`.
624
358112
    fn preference_order(&self) -> impl Iterator<Item = (ListKind, &Guard)> + '_ {
625
358112
        self.preference_order_ids()
626
372118
            .filter_map(move |(p, id)| self.guards.by_all_ids(id).map(|g| (p, g)))
627
358112
    }
628

            
629
    /// Return true if `guard_id` is an identity subset for any primary guard in this set.
630
358272
    fn guard_is_primary(&self, guard_id: &GuardId) -> bool {
631
358272
        // (This could be yes/no/maybe.)
632
358272

            
633
358272
        // This is O(n), but the list is short.
634
358272
        self.primary
635
358272
            .iter()
636
381695
            .any(|p| p.has_all_relay_ids_from(guard_id))
637
358272
    }
638

            
639
    /// For every guard that has been marked as `Unreachable` for too long,
640
    /// mark it as `Unknown`.
641
348506
    pub(crate) fn consider_all_retries(&mut self, now: Instant) {
642
348506
        let old_guards = std::mem::take(&mut self.guards);
643
348506
        self.guards = old_guards
644
348506
            .into_values()
645
3398838
            .map(|mut guard| {
646
3390296
                guard.consider_retry(now);
647
3390296
                guard
648
3398838
            })
649
348506
            .collect();
650
348506
    }
651

            
652
    /// Return the earliest time at which any guard will be retriable.
653
9432
    pub(crate) fn next_retry(&self, usage: &GuardUsage) -> Option<Instant> {
654
9432
        self.guards
655
9432
            .values()
656
9441
            .filter_map(|g| g.next_retry(usage))
657
9432
            .min()
658
9432
    }
659

            
660
    /// Mark every `Unreachable` primary guard as `Unknown`.
661
2
    pub(crate) fn mark_primary_guards_retriable(&mut self) {
662
6
        for id in &self.primary {
663
4
            self.guards
664
6
                .modify_by_all_ids(id, |guard| guard.mark_retriable());
665
4
        }
666
2
    }
667

            
668
    /// Return true if all of our primary guards are currently marked
669
    /// unreachable.
670
8
    pub(crate) fn all_primary_guards_are_unreachable(&mut self) -> bool {
671
8
        self.primary
672
8
            .iter()
673
16
            .flat_map(|id| self.guards.by_all_ids(id))
674
16
            .all(|g| g.reachable() == Reachable::Unreachable)
675
8
    }
676

            
677
    /// Mark every `Unreachable` guard as `Unknown`.
678
    pub(crate) fn mark_all_guards_retriable(&mut self) {
679
        let old_guards = std::mem::take(&mut self.guards);
680
        self.guards = old_guards
681
            .into_values()
682
            .map(|mut guard| {
683
                guard.mark_retriable();
684
                guard
685
            })
686
            .collect();
687
    }
688

            
689
    /// Record that an attempt has begun to use the guard with
690
    /// `guard_id`.
691
348526
    pub(crate) fn record_attempt(&mut self, guard_id: &GuardId, now: Instant) {
692
348526
        let is_primary = self.guard_is_primary(guard_id);
693
357078
        self.guards.modify_by_all_ids(guard_id, |guard| {
694
348526
            guard.record_attempt(now);
695
348526

            
696
348526
            if !is_primary {
697
26
                guard.note_exploratory_circ(true);
698
348500
            }
699
357078
        });
700
348526
    }
701

            
702
    /// Record that an attempt to use the guard with `guard_id` has just
703
    /// succeeded.
704
    ///
705
    /// If `how` is provided, it's an operation from outside the crate that the
706
    /// guard succeeded at doing.
707
9580
    pub(crate) fn record_success(
708
9580
        &mut self,
709
9580
        guard_id: &GuardId,
710
9580
        params: &GuardParams,
711
9580
        how: Option<ExternalActivity>,
712
9580
        now: SystemTime,
713
9580
    ) {
714
9580
        self.assert_consistency();
715
9846
        self.guards.modify_by_all_ids(guard_id, |guard| match how {
716
8
            Some(external) => guard.record_external_success(external),
717
            None => {
718
9572
                let newly_confirmed = guard.record_success(now, params);
719
9572

            
720
9572
                if newly_confirmed == NewlyConfirmed::Yes {
721
708
                    self.confirmed.push(guard_id.clone());
722
708
                    self.primary_guards_invalidated = true;
723
8908
                }
724
            }
725
9846
        });
726
9580
        self.assert_consistency();
727
9580
    }
728

            
729
    /// Record that an attempt to use the guard with `guard_id` has just failed.
730
    ///
731
54
    pub(crate) fn record_failure(
732
54
        &mut self,
733
54
        guard_id: &GuardId,
734
54
        how: Option<ExternalActivity>,
735
54
        now: Instant,
736
54
    ) {
737
54
        // TODO use instant uniformly for in-process, and systemtime for storage?
738
54
        let is_primary = self.guard_is_primary(guard_id);
739
81
        self.guards.modify_by_all_ids(guard_id, |guard| match how {
740
8
            Some(external) => guard.record_external_failure(external, now),
741
46
            None => guard.record_failure(now, is_primary),
742
81
        });
743
54
    }
744

            
745
    /// Record that an attempt to use the guard with `guard_id` has
746
    /// just been abandoned, without learning whether it succeeded or failed.
747
328831
    pub(crate) fn record_attempt_abandoned(&mut self, guard_id: &GuardId) {
748
328831
        self.guards
749
336915
            .modify_by_all_ids(guard_id, |guard| guard.note_exploratory_circ(false));
750
328831
    }
751

            
752
    /// Record that an attempt to use the guard with `guard_id` has
753
    /// just failed in a way that we could not definitively attribute to
754
    /// the guard.
755
    pub(crate) fn record_indeterminate_result(&mut self, guard_id: &GuardId) {
756
        self.guards.modify_by_all_ids(guard_id, |guard| {
757
            guard.note_exploratory_circ(false);
758
            guard.record_indeterminate_result();
759
        });
760
    }
761

            
762
    /// Record that a given guard has told us about clock skew.
763
    pub(crate) fn record_skew(&mut self, guard_id: &GuardId, observation: SkewObservation) {
764
        self.guards
765
            .modify_by_all_ids(guard_id, |guard| guard.note_skew(observation));
766
    }
767

            
768
    /// Return an iterator over all stored clock skew observations.
769
    pub(crate) fn skew_observations(&self) -> impl Iterator<Item = &SkewObservation> {
770
        self.guards.values().filter_map(|g| g.skew())
771
    }
772

            
773
    /// Return whether the circuit manager can be allowed to use a
774
    /// circuit with the `guard_id`.
775
    ///
776
    /// Return `Some(bool)` if the circuit is usable, and `None` if we
777
    /// cannot yet be sure.
778
9692
    pub(crate) fn circ_usability_status(
779
9692
        &self,
780
9692
        guard_id: &GuardId,
781
9692
        usage: &GuardUsage,
782
9692
        params: &GuardParams,
783
9692
        now: Instant,
784
9692
    ) -> Option<bool> {
785
9692
        // TODO-SPEC: This isn't what the spec says.  The spec is phrased
786
9692
        // in terms of circuits blocking circuits, whereas this algorithm is
787
9692
        // about guards blocking guards.
788
9692
        //
789
9692
        // Also notably, the spec also says:
790
9692
        //
791
9692
        // * Among guards that do not appear in {CONFIRMED_GUARDS},
792
9692
        // {is_pending}==true guards have higher priority.
793
9692
        // * Among those, the guard with earlier {last_tried_connect} time
794
9692
        // has higher priority.
795
9692
        // * Finally, among guards that do not appear in
796
9692
        // {CONFIRMED_GUARDS} with {is_pending==false}, all have equal
797
9692
        // priority.
798
9692
        //
799
9692
        // I believe this approach is fine too, but we ought to document it.
800
9692

            
801
9692
        if self.guard_is_primary(guard_id) {
802
            // Circuits built to primary guards are always usable immediately.
803
            //
804
            // This has to be a special case, since earlier primary guards
805
            // don't block later ones.
806
9676
            return Some(true);
807
16
        }
808
16

            
809
16
        // Assuming that the guard is _not_ primary, then the rule is
810
16
        // fairly simple: we can use the guard if all the guards we'd
811
16
        // _rather_ use are either down, or have had their circuit
812
16
        // attempts pending for too long.
813
16

            
814
16
        let cutoff = now
815
16
            .checked_sub(params.np_connect_timeout)
816
16
            .expect("Can't subtract connect timeout from now.");
817

            
818
40
        for (src, guard) in self.preference_order() {
819
40
            if guard.guard_id() == guard_id {
820
10
                return Some(true);
821
30
            }
822
30
            if guard.usable() && self.active_filter.permits(guard) && guard.conforms_to_usage(usage)
823
            {
824
30
                match (src, guard.reachable()) {
825
4
                    (_, Reachable::Reachable) => return Some(false),
826
24
                    (_, Reachable::Unreachable) => (),
827
                    (ListKind::Primary, Reachable::Untried | Reachable::Retriable) => {
828
                        return Some(false)
829
                    }
830
                    (_, Reachable::Untried | Reachable::Retriable) => {
831
2
                        if guard.exploratory_attempt_after(cutoff) {
832
2
                            return None;
833
                        }
834
                    }
835
                }
836
            }
837
        }
838

            
839
        // This guard is not even listed.
840
        Some(false)
841
9692
    }
842

            
843
    /// Try to select a guard for a given `usage`.
844
    ///
845
    /// On success, returns the kind of guard that we got, and its filtered
846
    /// representation in a form suitable for use as a first hop.
847
    ///
848
    /// Label the returned guard as having come from `sample_id`.
849
    //
850
    // NOTE (nickm): I wish that we didn't have to take sample_id as an input,
851
    // but the alternative would be storing it as a member of `GuardSet`, which
852
    // makes things very complicated.
853
357936
    pub(crate) fn pick_guard(
854
357936
        &self,
855
357936
        sample_id: &GuardSetSelector,
856
357936
        usage: &GuardUsage,
857
357936
        params: &GuardParams,
858
357936
        now: Instant,
859
357936
    ) -> Result<(ListKind, FirstHop), PickGuardError> {
860
357936
        let (list_kind, id) = self.pick_guard_id(usage, params, now)?;
861
348506
        let first_hop = self
862
348506
            .get(&id)
863
348506
            .expect("Somehow selected a guard we don't know!")
864
348506
            .get_external_rep(sample_id.clone());
865
348506
        let first_hop = self.active_filter.modify_hop(first_hop)?;
866

            
867
348506
        Ok((list_kind, first_hop))
868
357936
    }
869

            
870
    /// Try to select a guard for a given `usage`.
871
    ///
872
    /// On success, returns the kind of guard that we got, and its identity.
873
358096
    fn pick_guard_id(
874
358096
        &self,
875
358096
        usage: &GuardUsage,
876
358096
        params: &GuardParams,
877
358096
        now: Instant,
878
358096
    ) -> Result<(ListKind, GuardId), PickGuardError> {
879
358096
        debug_assert!(!self.primary_guards_invalidated);
880
358096
        let n_options = match usage.kind {
881
6868
            GuardUsageKind::OneHopDirectory => params.dir_parallelism,
882
351228
            GuardUsageKind::Data => params.data_parallelism,
883
        };
884

            
885
        // Counts of how many elements were rejected by which of the filters
886
        // below.
887
        //
888
        // Note that since we use `Iterator::take`, these counts won't cover the
889
        // whole guard sample on the successful case: only in the failing case,
890
        // when we fail to find any candidates.
891
358096
        let mut running = FilterCount::default();
892
358096
        let mut pending = FilterCount::default();
893
358096
        let mut suitable = FilterCount::default();
894
358096
        let mut filtered = FilterCount::default();
895
358096

            
896
358096
        let mut options: Vec<_> = self
897
358096
            .preference_order()
898
358096
            // Discard the guards that are down or unusable, and see if any
899
358096
            // are left.
900
372070
            .filter_cnt(&mut running, |(_, g)| {
901
363218
                g.usable()
902
363218
                    && g.reachable() != Reachable::Unreachable
903
362878
                    && g.ready_for_usage(usage, now)
904
372070
            })
905
358096
            // Now remove those that are excluded because we're already trying
906
358096
            // them on an exploratory basis.
907
371722
            .filter_cnt(&mut pending, |(_, g)| !g.exploratory_circ_pending())
908
358096
            // ...or because they don't support the operation we're
909
358096
            // attempting...
910
371712
            .filter_cnt(&mut suitable, |(_, g)| g.conforms_to_usage(usage))
911
358096
            // ... or because we specifically filtered them out.
912
371220
            .filter_cnt(&mut filtered, |(_, g)| self.active_filter.permits(*g))
913
358096
            // We only consider the first n_options such guards.
914
358096
            .take(n_options)
915
358096
            .collect();
916
358096

            
917
366717
        if options.iter().any(|(src, _)| src.is_primary()) {
918
348638
            // If there are any primary guards, we only consider those.
919
370950
            options.retain(|(src, _)| src.is_primary());
920
348638
        } else {
921
9458
            // If there are no primary guards, parallelism doesn't apply.
922
9458
            options.truncate(1);
923
9458
        }
924

            
925
358096
        match options.choose(&mut rand::rng()) {
926
348664
            Some((src, g)) => Ok((*src, g.guard_id().clone())),
927
            None => {
928
9432
                let retry_at = if running.n_accepted == 0 {
929
9432
                    self.next_retry(usage)
930
                } else {
931
                    None
932
                };
933
9432
                Err(PickGuardError::AllGuardsDown {
934
9432
                    retry_at,
935
9432
                    running,
936
9432
                    pending,
937
9432
                    suitable,
938
9432
                    filtered,
939
9432
                })
940
            }
941
        }
942
358096
    }
943

            
944
    /// Return the guards whose bridge descriptors we should request, given our
945
    /// current configuration and status.
946
    ///
947
    /// (The output of this function is not reasonable unless this is a Bridge
948
    /// sample.)
949
    #[cfg(feature = "bridge-client")]
950
    pub(crate) fn descriptors_to_request(&self, now: Instant, params: &GuardParams) -> Vec<&Guard> {
951
        /// This constant is here to improve our odds that we can get a working
952
        /// bridge if we have any per-circuit filters that would prevent us from
953
        /// using our preferred bridge.
954
        const MINIMUM: usize = 2;
955

            
956
        let maximum = std::cmp::max(params.data_parallelism, MINIMUM);
957
        let data_usage = GuardUsage::default();
958

            
959
        // Here we duplicate some but not all of the restrictions above in
960
        // pick_guard_id.  We skip those restrictions that are specific to only
961
        // certain kinds of circuits, and those that are temporary restrictions
962
        // encouraging us to try more guards.
963
        //
964
        // TODO: we may want to refactor this code and the code in pick_guard_id
965
        // above to share a single function.  Before we do that, however, I want
966
        // to experiment with this logic a bit to make sure that it works and
967
        // doesn't give us surprising results.
968
        self.preference_order()
969
            .filter(|(_, g)| {
970
                g.usable()
971
                    && g.reachable() != Reachable::Unreachable
972
                    && g.ready_for_usage(&data_usage, now)
973
                    && self.active_filter.permits(*g)
974
            })
975
            .take(maximum)
976
            .map(|(_, g)| g)
977
            .collect()
978
    }
979
}
980

            
981
use serde::Serializer;
982
use tor_persist::JsonValue;
983

            
984
/// State object used to serialize and deserialize a [`GuardSet`].
985
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
986
pub(crate) struct GuardSample<'a> {
987
    /// Equivalent to `GuardSet.guards.values()`, except in sample order.
988
    guards: Vec<Cow<'a, Guard>>,
989
    /// The identities for the confirmed members of `guards`, in confirmed order.
990
    confirmed: Cow<'a, [GuardId]>,
991
    /// Other data from the state file that this version of Arti doesn't recognize.
992
    #[serde(flatten)]
993
    remaining: HashMap<String, JsonValue>,
994
}
995

            
996
impl Serialize for GuardSet {
997
60
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
998
60
    where
999
60
        S: Serializer,
60
    {
60
        GuardSample::from(self).serialize(serializer)
60
    }
}
impl<'a> From<&'a GuardSet> for GuardSample<'a> {
764
    fn from(guards: &'a GuardSet) -> Self {
764
        guards.get_state()
764
    }
}
impl<'a> From<GuardSample<'a>> for GuardSet {
26
    fn from(sample: GuardSample) -> Self {
26
        GuardSet::from_state(sample)
26
    }
}
#[cfg(test)]
mod test {
    // @@ begin test lint list maintained by maint/add_warning @@
    #![allow(clippy::bool_assert_comparison)]
    #![allow(clippy::clone_on_copy)]
    #![allow(clippy::dbg_macro)]
    #![allow(clippy::mixed_attributes_style)]
    #![allow(clippy::print_stderr)]
    #![allow(clippy::print_stdout)]
    #![allow(clippy::single_char_pattern)]
    #![allow(clippy::unwrap_used)]
    #![allow(clippy::unchecked_duration_subtraction)]
    #![allow(clippy::useless_vec)]
    #![allow(clippy::needless_pass_by_value)]
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
    use tor_linkspec::{HasRelayIds, RelayIdType};
    use tor_netdir::NetDir;
    use tor_netdoc::doc::netstatus::{RelayFlags, RelayWeight};
    use super::*;
    use crate::FirstHopId;
    use std::time::Duration;
    fn netdir() -> NetDir {
        use tor_netdir::testnet;
        testnet::construct_netdir().unwrap_if_sufficient().unwrap()
    }
    #[test]
    fn sample_test() {
        // Make a test network that gives every relay equal weight, and which
        // has 20 viable (Guard + V2Dir + DirCache=2) candidates.  Otherwise the
        // calculation of collision probability at the end of this function is
        // too tricky.
        let netdir = tor_netdir::testnet::construct_custom_netdir(|idx, builder, _| {
            // Give every node equal bandwidth.
            builder.rs.weight(RelayWeight::Measured(1000));
            // The default network has 40 relays, and the first 10 are
            // not Guard by default.
            if idx >= 10 {
                builder.rs.add_flags(RelayFlags::GUARD);
                if idx >= 20 {
                    builder.rs.protos("DirCache=2".parse().unwrap());
                } else {
                    builder.rs.protos("".parse().unwrap());
                }
            }
        })
        .unwrap()
        .unwrap_if_sufficient()
        .unwrap();
        // Make sure that we got the numbers we expected.
        assert_eq!(40, netdir.relays().count());
        assert_eq!(
            30,
            netdir
                .relays()
                .filter(|r| r.low_level_details().is_suitable_as_guard())
                .count()
        );
        assert_eq!(
            20,
            netdir
                .relays()
                .filter(|r| r.low_level_details().is_suitable_as_guard()
                    && r.low_level_details().is_dir_cache())
                .count()
        );
        let params = GuardParams {
            min_filtered_sample_size: 5,
            max_sample_bw_fraction: 1.0,
            ..GuardParams::default()
        };
        let mut samples: Vec<HashSet<GuardId>> = Vec::new();
        for _ in 0..3 {
            let mut guards = GuardSet::default();
            guards.extend_sample_as_needed(SystemTime::now(), &params, &netdir);
            assert_eq!(guards.guards.len(), params.min_filtered_sample_size);
            assert_eq!(guards.confirmed.len(), 0);
            assert_eq!(guards.primary.len(), 0);
            guards.assert_consistency();
            // make sure all the guards are okay.
            for guard in guards.guards.values() {
                let id = FirstHopId::in_sample(GuardSetSelector::Default, guard.guard_id().clone());
                let relay = id.get_relay(&netdir).unwrap();
                assert!(relay.low_level_details().is_suitable_as_guard());
                assert!(relay.low_level_details().is_dir_cache());
                assert!(guards.guards.by_all_ids(&relay).is_some());
                {
                    assert!(!guard.is_expired(&params, SystemTime::now()));
                }
            }
            // Make sure that the sample doesn't expand any further.
            guards.extend_sample_as_needed(SystemTime::now(), &params, &netdir);
            assert_eq!(guards.guards.len(), params.min_filtered_sample_size);
            guards.assert_consistency();
            samples.push(guards.sample.into_iter().collect());
        }
        // The probability of getting the same sample 3 times in a row is (20 choose 5)^-2,
        // which is pretty low.  (About 1 in 240 million.)
        assert!(samples[0] != samples[1] || samples[1] != samples[2]);
    }
    #[test]
    fn persistence() {
        let netdir = netdir();
        let params = GuardParams {
            min_filtered_sample_size: 5,
            ..GuardParams::default()
        };
        let t1 = SystemTime::now();
        let t2 = t1 + Duration::from_secs(20);
        let mut guards = GuardSet::default();
        guards.extend_sample_as_needed(t1, &params, &netdir);
        // Pick a guard and mark it as confirmed.
        let id1 = guards.sample[0].clone();
        guards.record_success(&id1, &params, None, t2);
        assert_eq!(&guards.confirmed, &[id1.clone()]);
        // Encode the guards, then decode them.
        let state: GuardSample = (&guards).into();
        let guards2: GuardSet = state.into();
        assert_eq!(&guards2.sample, &guards.sample);
        assert_eq!(&guards2.confirmed, &guards.confirmed);
        assert_eq!(&guards2.confirmed, &[id1]);
        assert_eq!(
            guards
                .guards
                .values()
                .map(Guard::guard_id)
                .collect::<HashSet<_>>(),
            guards2
                .guards
                .values()
                .map(Guard::guard_id)
                .collect::<HashSet<_>>()
        );
        for g in guards.guards.values() {
            let g2 = guards2.guards.by_all_ids(g.guard_id()).unwrap();
            assert_eq!(format!("{:?}", g), format!("{:?}", g2));
        }
    }
    #[test]
    fn select_primary() {
        let netdir = netdir();
        let params = GuardParams {
            min_filtered_sample_size: 5,
            n_primary: 4,
            ..GuardParams::default()
        };
        let t1 = SystemTime::now();
        let t2 = t1 + Duration::from_secs(20);
        let t3 = t2 + Duration::from_secs(30);
        let mut guards = GuardSet::default();
        guards.extend_sample_as_needed(t1, &params, &netdir);
        // Pick a guard and mark it as confirmed.
        let id3 = guards.sample[3].clone();
        guards.record_success(&id3, &params, None, t2);
        assert_eq!(&guards.confirmed, &[id3.clone()]);
        let id1 = guards.sample[1].clone();
        guards.record_success(&id1, &params, None, t3);
        assert_eq!(&guards.confirmed, &[id3.clone(), id1.clone()]);
        // Select primary guards and make sure we're obeying the rules.
        guards.select_primary_guards(&params);
        assert_eq!(guards.primary.len(), 4);
        assert_eq!(&guards.primary[0], &id3);
        assert_eq!(&guards.primary[1], &id1);
        let p3 = guards.primary[2].clone();
        let p4 = guards.primary[3].clone();
        assert_eq!(
            [id1.clone(), id3.clone(), p3.clone(), p4.clone()]
                .iter()
                .unique()
                .count(),
            4
        );
        // Mark another guard as confirmed and see that the list changes to put
        // that guard right after the previously confirmed guards, but we keep
        // one of the previous unconfirmed primary guards.
        guards.record_success(&p4, &params, None, t3);
        assert_eq!(&guards.confirmed, &[id3.clone(), id1.clone(), p4.clone()]);
        guards.select_primary_guards(&params);
        assert_eq!(guards.primary.len(), 4);
        assert_eq!(&guards.primary[0], &id3);
        assert_eq!(&guards.primary[1], &id1);
        assert_eq!(&guards.primary, &[id3, id1, p4, p3]);
    }
    #[test]
    fn expiration() {
        let netdir = netdir();
        let params = GuardParams::default();
        let t1 = SystemTime::now();
        let mut guards = GuardSet::default();
        guards.extend_sample_as_needed(t1, &params, &netdir);
        // note that there are only 10 Guard+V2Dir nodes in the netdir().
        assert_eq!(guards.sample.len(), 10);
        // Mark one guard as confirmed; it will have a different timeout.
        // Pick a guard and mark it as confirmed.
        let id1 = guards.sample[0].clone();
        guards.record_success(&id1, &params, None, t1);
        assert_eq!(&guards.confirmed, &[id1]);
        let one_day = Duration::from_secs(86400);
        guards.expire_old_guards(&params, t1 + one_day * 30);
        assert_eq!(guards.sample.len(), 10); // nothing has expired.
        // This is long enough to make sure that the confirmed guard has expired.
        guards.expire_old_guards(&params, t1 + one_day * 70);
        assert_eq!(guards.sample.len(), 9);
        guards.expire_old_guards(&params, t1 + one_day * 200);
        assert_eq!(guards.sample.len(), 0);
    }
    #[test]
    #[allow(clippy::cognitive_complexity)]
    fn sampling_and_usage() {
        let netdir = netdir();
        let params = GuardParams {
            min_filtered_sample_size: 5,
            n_primary: 2,
            ..GuardParams::default()
        };
        let st1 = SystemTime::now();
        let i1 = Instant::now();
        let sec = Duration::from_secs(1);
        let mut guards = GuardSet::default();
        guards.extend_sample_as_needed(st1, &params, &netdir);
        guards.select_primary_guards(&params);
        // First guard: try it, and let it fail.
        let usage = crate::GuardUsageBuilder::default().build().unwrap();
        let id1 = guards.primary[0].clone();
        let id2 = guards.primary[1].clone();
        let (src, id) = guards.pick_guard_id(&usage, &params, i1).unwrap();
        assert_eq!(src, ListKind::Primary);
        assert_eq!(&id, &id1);
        guards.record_attempt(&id, i1);
        guards.record_failure(&id, None, i1 + sec);
        // Second guard: try it, and try it again, and have it fail.
        let (src, id) = guards.pick_guard_id(&usage, &params, i1 + sec).unwrap();
        assert_eq!(src, ListKind::Primary);
        assert_eq!(&id, &id2);
        guards.record_attempt(&id, i1 + sec);
        let (src, id_x) = guards.pick_guard_id(&usage, &params, i1 + sec).unwrap();
        // We get the same guard this (second) time that we pick it too, since
        // it is a primary guard, and is_pending won't block it.
        assert_eq!(id_x, id);
        assert_eq!(src, ListKind::Primary);
        guards.record_attempt(&id_x, i1 + sec * 2);
        guards.record_failure(&id_x, None, i1 + sec * 3);
        guards.record_failure(&id, None, i1 + sec * 4);
        // Third guard: this one won't be primary.
        let (src, id3) = guards.pick_guard_id(&usage, &params, i1 + sec * 4).unwrap();
        assert_eq!(src, ListKind::Sample);
        assert!(!guards.primary.contains(&id3));
        guards.record_attempt(&id3, i1 + sec * 5);
        // Fourth guard: Third guard will be pending, so a different one gets
        // handed out here.
        let (src, id4) = guards.pick_guard_id(&usage, &params, i1 + sec * 5).unwrap();
        assert_eq!(src, ListKind::Sample);
        assert!(id3 != id4);
        assert!(!guards.primary.contains(&id4));
        guards.record_attempt(&id4, i1 + sec * 6);
        // Look at usability status: primary guards should be usable
        // immediately; third guard should be too (since primary
        // guards are down).  Fourth should not have a known status,
        // since third is pending.
        assert_eq!(
            guards.circ_usability_status(&id1, &usage, &params, i1 + sec * 6),
            Some(true)
        );
        assert_eq!(
            guards.circ_usability_status(&id2, &usage, &params, i1 + sec * 6),
            Some(true)
        );
        assert_eq!(
            guards.circ_usability_status(&id3, &usage, &params, i1 + sec * 6),
            Some(true)
        );
        assert_eq!(
            guards.circ_usability_status(&id4, &usage, &params, i1 + sec * 6),
            None
        );
        // Have both guards succeed.
        guards.record_success(&id3, &params, None, st1 + sec * 7);
        guards.record_success(&id4, &params, None, st1 + sec * 8);
        // Check the impact of having both guards succeed.
        assert!(guards.primary_guards_invalidated);
        guards.select_primary_guards(&params);
        assert_eq!(&guards.primary, &[id3.clone(), id4.clone()]);
        // Next time we ask for a guard, we get a primary guard again.
        let (src, id) = guards
            .pick_guard_id(&usage, &params, i1 + sec * 10)
            .unwrap();
        assert_eq!(src, ListKind::Primary);
        assert_eq!(&id, &id3);
        // If we ask for a directory guard, we get one of the primaries.
        let mut found = HashSet::new();
        let usage = crate::GuardUsageBuilder::default()
            .kind(crate::GuardUsageKind::OneHopDirectory)
            .build()
            .unwrap();
        for _ in 0..64 {
            let (src, id) = guards
                .pick_guard_id(&usage, &params, i1 + sec * 10)
                .unwrap();
            assert_eq!(src, ListKind::Primary);
            assert_eq!(
                guards.circ_usability_status(&id, &usage, &params, i1 + sec * 10),
                Some(true)
            );
            guards.record_attempt_abandoned(&id);
            found.insert(id);
        }
        assert!(found.len() == 2);
        assert!(found.contains(&id3));
        assert!(found.contains(&id4));
        // Since the primaries are now up, other guards are not usable.
        assert_eq!(
            guards.circ_usability_status(&id1, &usage, &params, i1 + sec * 12),
            Some(false)
        );
        assert_eq!(
            guards.circ_usability_status(&id2, &usage, &params, i1 + sec * 12),
            Some(false)
        );
    }
    #[test]
    fn everybodys_down() {
        let netdir = netdir();
        let params = GuardParams {
            min_filtered_sample_size: 5,
            n_primary: 2,
            max_sample_bw_fraction: 1.0,
            ..GuardParams::default()
        };
        let mut st = SystemTime::now();
        let mut inst = Instant::now();
        let sec = Duration::from_secs(1);
        let usage = crate::GuardUsageBuilder::default().build().unwrap();
        let mut guards = GuardSet::default();
        guards.extend_sample_as_needed(st, &params, &netdir);
        guards.select_primary_guards(&params);
        assert_eq!(guards.sample.len(), 5);
        for _ in 0..5 {
            let (_, id) = guards.pick_guard_id(&usage, &params, inst).unwrap();
            guards.record_attempt(&id, inst);
            guards.record_failure(&id, None, inst + sec);
            inst += sec * 2;
            st += sec * 2;
        }
        let e = guards.pick_guard_id(&usage, &params, inst);
        assert!(matches!(e, Err(PickGuardError::AllGuardsDown { .. })));
        // Now in theory we should re-grow when we extend.
        guards.extend_sample_as_needed(st, &params, &netdir);
        guards.select_primary_guards(&params);
        assert_eq!(guards.sample.len(), 10);
    }
    #[test]
    fn retry_primary() {
        let netdir = netdir();
        let params = GuardParams {
            min_filtered_sample_size: 5,
            n_primary: 2,
            max_sample_bw_fraction: 1.0,
            ..GuardParams::default()
        };
        let usage = crate::GuardUsageBuilder::default().build().unwrap();
        let mut guards = GuardSet::default();
        guards.extend_sample_as_needed(SystemTime::now(), &params, &netdir);
        guards.select_primary_guards(&params);
        assert_eq!(guards.primary.len(), 2);
        assert!(!guards.all_primary_guards_are_unreachable());
        // Let one primary guard fail.
        let (kind, p_id1) = guards
            .pick_guard_id(&usage, &params, Instant::now())
            .unwrap();
        assert_eq!(kind, ListKind::Primary);
        guards.record_failure(&p_id1, None, Instant::now());
        assert!(!guards.all_primary_guards_are_unreachable());
        // Now let the other one fail.
        let (kind, p_id2) = guards
            .pick_guard_id(&usage, &params, Instant::now())
            .unwrap();
        assert_eq!(kind, ListKind::Primary);
        guards.record_failure(&p_id2, None, Instant::now());
        assert!(guards.all_primary_guards_are_unreachable());
        // Now mark the guards retriable.
        guards.mark_primary_guards_retriable();
        assert!(!guards.all_primary_guards_are_unreachable());
        let (kind, p_id3) = guards
            .pick_guard_id(&usage, &params, Instant::now())
            .unwrap();
        assert_eq!(kind, ListKind::Primary);
        assert_eq!(p_id3, p_id1);
    }
    #[test]
    fn count_missing_mds() {
        let netdir = netdir();
        let params = GuardParams {
            min_filtered_sample_size: 5,
            n_primary: 2,
            max_sample_bw_fraction: 1.0,
            ..GuardParams::default()
        };
        let usage = crate::GuardUsageBuilder::default().build().unwrap();
        let mut guards = GuardSet::default();
        guards.extend_sample_as_needed(SystemTime::now(), &params, &netdir);
        guards.select_primary_guards(&params);
        assert_eq!(guards.primary.len(), 2);
        let (_kind, p_id1) = guards
            .pick_guard_id(&usage, &params, Instant::now())
            .unwrap();
        guards.record_success(&p_id1, &params, None, SystemTime::now());
        assert_eq!(guards.n_primary_without_id_info_in(&netdir), 0);
        use tor_netdir::testnet;
        let netdir2 = testnet::construct_custom_netdir(|_idx, bld, _| {
            let md_so_far = bld.md.testing_md().expect("Couldn't build md?");
            if &p_id1.0.identity(RelayIdType::Ed25519).unwrap() == md_so_far.ed25519_id() {
                bld.omit_md = true;
            }
        })
        .unwrap()
        .unwrap_if_sufficient()
        .unwrap();
        assert_eq!(guards.n_primary_without_id_info_in(&netdir2), 1);
    }
    #[test]
    fn copy_status() {
        let netdir = netdir();
        let params = GuardParams {
            min_filtered_sample_size: 5,
            n_primary: 2,
            max_sample_bw_fraction: 1.0,
            ..GuardParams::default()
        };
        let mut guards1 = GuardSet::default();
        guards1.extend_sample_as_needed(SystemTime::now(), &params, &netdir);
        guards1.select_primary_guards(&params);
        let mut guards2 = guards1.clone();
        // Make a persistent change in guards1, and a different persistent change in guards2.
        let id1 = guards1.primary[0].clone();
        let id2 = guards1.primary[1].clone();
        guards1.record_success(&id1, &params, None, SystemTime::now());
        guards2.record_success(&id2, &params, None, SystemTime::now());
        // Make a non-persistent change in guards2.
        guards2.record_failure(&id2, None, Instant::now());
        // Copy status: make sure non-persistent status changed, and  persistent didn't.
        guards1.copy_ephemeral_status_into_newly_loaded_state(guards2);
        {
            let g1 = guards1.get(&id1).unwrap();
            let g2 = guards1.get(&id2).unwrap();
            assert!(g1.confirmed());
            assert!(!g2.confirmed());
            assert_eq!(g1.reachable(), Reachable::Untried);
            assert_eq!(g2.reachable(), Reachable::Unreachable);
        }
        // Now make a new set of unrelated guards, and make sure that copying
        // from it doesn't change the membership of guards1.
        let mut guards3 = GuardSet::default();
        let g1_set: HashSet<_> = guards1
            .guards
            .values()
            .map(|g| g.guard_id().clone())
            .collect();
        let mut g3_set: HashSet<_> = HashSet::new();
        for _ in 0..4 {
            // There is roughly a 1-in-5000 chance of getting the same set
            // twice, so we loop until that doesn't happen.
            guards3.extend_sample_as_needed(SystemTime::now(), &params, &netdir);
            guards3.select_primary_guards(&params);
            g3_set = guards3
                .guards
                .values()
                .map(|g| g.guard_id().clone())
                .collect();
            // There is roughly a 1-in-5000 chance of getting the same set twice, so
            if g1_set == g3_set {
                guards3 = GuardSet::default();
                continue;
            }
            break;
        }
        assert_ne!(g1_set, g3_set);
        // Do the copy; make sure that the membership is unchanged.
        guards1.copy_ephemeral_status_into_newly_loaded_state(guards3);
        let g1_set_new: HashSet<_> = guards1
            .guards
            .values()
            .map(|g| g.guard_id().clone())
            .collect();
        assert_eq!(g1_set, g1_set_new);
    }
}