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
    ExternalActivity, GuardParams, GuardUsage, GuardUsageKind, PickGuardError, ids::GuardId,
11
};
12
use crate::{FirstHop, GuardSetSelector};
13
use tor_basic_utils::iter::{FilterCount, IteratorExt as _};
14
use tor_basic_utils::onionperf_types::{OnionperfEvent, OnionperfGuardStatus};
15
use tor_linkspec::{ByRelayIds, HasRelayIds};
16

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
239
540
        self.assert_consistency();
240

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

            
250
540
        self.primary_guards_invalidated = true;
251
540
    }
252

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

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

            
272
10
                if let Some(other_guard) = other.guards.remove_exact(id) {
273
8
                    guard.copy_ephemeral_status_into_newly_loaded_state(other_guard)
274
                } else {
275
2
                    guard
276
                }
277
10
            })
278
2
            .collect();
279
2
    }
280

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

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

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

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

            
334
19
        guard_set
335
19
    }
336

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

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

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

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

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

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

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

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

            
464
    /// Add `relay` as a new guard.
465
    ///
466
    /// Does nothing if it is already a guard.
467
117124
    fn add_guard(&mut self, relay: Candidate, now: SystemTime, params: &GuardParams) {
468
117124
        let id = GuardId::from_relay_ids(&relay.owned_target);
469
117124
        if self.guards.by_all_ids(&id).is_some() {
470
            return;
471
117124
        }
472
117124
        debug!(guard_id=?id, "Adding guard to sample.");
473
        // TODO: I don't think onionperf actually needs the guard fingerpint,
474
        // but if it does, add it here.
475
117124
        trace!(
476
            onionperf = true,
477
            event = ?OnionperfEvent::Guard(OnionperfGuardStatus::New),
478
            guard_id = ?id,
479
        );
480
117124
        let guard = Guard::from_candidate(relay, now, params);
481
117124
        self.guards.insert(guard);
482
117124
        self.sample.push(id);
483
117124
        self.primary_guards_invalidated = true;
484
117124
    }
485

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

            
506
    /// Update the status of every guard  in this sample from a given source.
507
12042
    pub(crate) fn update_status_from_dir<U: Universe>(&mut self, dir: &U) {
508
12042
        let old_guards = std::mem::take(&mut self.guards);
509
12042
        self.guards = old_guards
510
12042
            .into_values()
511
12042
            .map(|mut guard| {
512
470
                guard.update_from_universe(dir);
513
470
                guard
514
470
            })
515
12042
            .collect();
516
        // Call "fix consistency", in case any guards got a new ID.
517
12042
        self.fix_consistency();
518
12042
    }
519

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

            
534
        // Only for logging.
535
381505
        let old_primary = self.primary.clone();
536

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

            
564
381505
        if self.primary != old_primary {
565
12281
            debug!(old=?old_primary, new=?self.primary, "Updated primary guards.");
566
369224
        }
567

            
568
        // Clear exploratory_circ_pending for all primary guards.
569
1126150
        for id in &self.primary {
570
1126150
            self.guards.modify_by_all_ids(id, |guard| {
571
1126150
                guard.note_exploratory_circ(false);
572
1126150
            });
573
        }
574

            
575
        // TODO: Recalculate retry times, perhaps, since we may have changed
576
        // the timeouts?
577

            
578
381505
        self.assert_consistency();
579
381505
        self.primary_guards_invalidated = false;
580
381505
    }
581

            
582
    /// Remove all guards which should expire `now`, according to the settings
583
    /// in `params`.
584
18367
    pub(crate) fn expire_old_guards(&mut self, params: &GuardParams, now: SystemTime) {
585
18367
        self.assert_consistency();
586
18367
        let n_pre = self.guards.len();
587
        // TODO: Might be more ideal to do this after the assert_consistency.
588
18403
        for guard in self.guards.values() {
589
5819
            if guard.is_expired(params, now) {
590
10
                tracing::trace!(
591
                    onionperf = true,
592
                    event = ?OnionperfEvent::Guard(OnionperfGuardStatus::Dropped),
593
                    guard_id = ?guard.guard_id(),
594
                );
595
5809
            }
596
        }
597
18403
        self.guards.retain(|g| !g.is_expired(params, now));
598
18367
        let guards = &self.guards;
599
18403
        self.sample.retain(|id| guards.by_all_ids(id).is_some());
600
18367
        self.confirmed.retain(|id| guards.by_all_ids(id).is_some());
601
18367
        self.primary.retain(|id| guards.by_all_ids(id).is_some());
602
18367
        self.assert_consistency();
603

            
604
18367
        if self.guards.len() < n_pre {
605
2
            let n_expired = n_pre - self.guards.len();
606
2
            debug!(n_expired, "Expired guards as too old.");
607
2
            self.primary_guards_invalidated = true;
608
18365
        }
609
18367
    }
610

            
611
    /// Return an iterator over the Id for every Guard in the sample that
612
    /// is not known to be Unreachable.
613
381505
    fn reachable_sample_ids(&self) -> impl Iterator<Item = &GuardId> {
614
381505
        self.sample.iter().filter(move |id| {
615
35059
            let g = self
616
35059
                .guards
617
35059
                .by_all_ids(*id)
618
35059
                .expect("Inconsistent guard state");
619
35059
            g.reachable() != Reachable::Unreachable
620
35059
        })
621
381505
    }
622

            
623
    /// Return an iterator that yields an element for every guard in
624
    /// this set, in preference order.
625
    ///
626
    /// Each element contains a `ListKind` that describes which list the
627
    /// guard was in, and a `&GuardId` that identifies the guard.
628
    ///
629
    /// Note that this function will return guards that are not
630
    /// accepted by the current active filter: the caller must apply
631
    /// that filter if appropriate.
632
566084
    fn preference_order_ids(&self) -> impl Iterator<Item = (ListKind, &GuardId)> {
633
566084
        self.primary
634
566084
            .iter()
635
577982
            .map(|id| (ListKind::Primary, id))
636
566084
            .chain(self.confirmed.iter().map(|id| (ListKind::Confirmed, id)))
637
566320
            .chain(self.sample.iter().map(|id| (ListKind::Sample, id)))
638
566084
            .unique_by(|(_, id)| *id)
639
566084
    }
640

            
641
    /// Like `preference_order_ids`, but yields `&Guard` instead of `&GuardId`.
642
566084
    fn preference_order(&self) -> impl Iterator<Item = (ListKind, &Guard)> + '_ {
643
566084
        self.preference_order_ids()
644
578212
            .filter_map(move |(p, id)| self.guards.by_all_ids(id).map(|g| (p, g)))
645
566084
    }
646

            
647
    /// Return true if `guard_id` is an identity subset for any primary guard in this set.
648
566221
    fn guard_is_primary(&self, guard_id: &GuardId) -> bool {
649
        // (This could be yes/no/maybe.)
650

            
651
        // This is O(n), but the list is short.
652
566221
        self.primary
653
566221
            .iter()
654
583924
            .any(|p| p.has_all_relay_ids_from(guard_id))
655
566221
    }
656

            
657
    /// For every guard that has been marked as `Unreachable` for too long,
658
    /// mark it as `Unknown`.
659
555874
    pub(crate) fn consider_all_retries(&mut self, now: Instant) {
660
555874
        let old_guards = std::mem::take(&mut self.guards);
661
555874
        self.guards = old_guards
662
555874
            .into_values()
663
5457192
            .map(|mut guard| {
664
5457192
                guard.consider_retry(now);
665
5457192
                guard
666
5457192
            })
667
555874
            .collect();
668
555874
    }
669

            
670
    /// Return the earliest time at which any guard will be retriable.
671
10121
    pub(crate) fn next_retry(&self, usage: &GuardUsage) -> Option<Instant> {
672
10121
        self.guards
673
10121
            .values()
674
10125
            .filter_map(|g| g.next_retry(usage))
675
10121
            .min()
676
10121
    }
677

            
678
    /// Mark every `Unreachable` primary guard as `Unknown`.
679
1
    pub(crate) fn mark_primary_guards_retriable(&mut self) {
680
2
        for id in &self.primary {
681
2
            self.guards
682
2
                .modify_by_all_ids(id, |guard| guard.mark_retriable());
683
        }
684
1
    }
685

            
686
    /// Return true if all of our primary guards are currently marked
687
    /// unreachable.
688
4
    pub(crate) fn all_primary_guards_are_unreachable(&mut self) -> bool {
689
4
        self.primary
690
4
            .iter()
691
6
            .flat_map(|id| self.guards.by_all_ids(id))
692
6
            .all(|g| g.reachable() == Reachable::Unreachable)
693
4
    }
694

            
695
    /// Mark every `Unreachable` guard as `Unknown`.
696
    pub(crate) fn mark_all_guards_retriable(&mut self) {
697
        let old_guards = std::mem::take(&mut self.guards);
698
        self.guards = old_guards
699
            .into_values()
700
            .map(|mut guard| {
701
                guard.mark_retriable();
702
                guard
703
            })
704
            .collect();
705
    }
706

            
707
    /// Record that an attempt has begun to use the guard with
708
    /// `guard_id`.
709
555884
    pub(crate) fn record_attempt(&mut self, guard_id: &GuardId, now: Instant) {
710
555884
        let is_primary = self.guard_is_primary(guard_id);
711
555884
        self.guards.modify_by_all_ids(guard_id, |guard| {
712
555884
            guard.record_attempt(now);
713

            
714
555884
            if !is_primary {
715
17
                guard.note_exploratory_circ(true);
716
555867
            }
717
555884
        });
718
555884
    }
719

            
720
    /// Record that an attempt to use the guard with `guard_id` has just
721
    /// succeeded.
722
    ///
723
    /// If `how` is provided, it's an operation from outside the crate that the
724
    /// guard succeeded at doing.
725
10248
    pub(crate) fn record_success(
726
10248
        &mut self,
727
10248
        guard_id: &GuardId,
728
10248
        params: &GuardParams,
729
10248
        how: Option<ExternalActivity>,
730
10248
        now: SystemTime,
731
10248
    ) {
732
10248
        self.assert_consistency();
733
10248
        self.guards.modify_by_all_ids(guard_id, |guard| match how {
734
6
            Some(external) => guard.record_external_success(external),
735
            None => {
736
10242
                let newly_confirmed = guard.record_success(now, params);
737

            
738
10242
                if newly_confirmed == NewlyConfirmed::Yes {
739
734
                    self.confirmed.push(guard_id.clone());
740
734
                    self.primary_guards_invalidated = true;
741
9534
                }
742
            }
743
10248
        });
744
10248
        self.assert_consistency();
745
10248
    }
746

            
747
    /// Record that an attempt to use the guard with `guard_id` has just failed.
748
    ///
749
35
    pub(crate) fn record_failure(
750
35
        &mut self,
751
35
        guard_id: &GuardId,
752
35
        how: Option<ExternalActivity>,
753
35
        now: Instant,
754
35
    ) {
755
35
        trace!(
756
            onionperf = true,
757
            event = ?OnionperfEvent::Guard(OnionperfGuardStatus::Down),
758
            ?guard_id,
759
        );
760
        // TODO use instant uniformly for in-process, and systemtime for storage?
761
35
        let is_primary = self.guard_is_primary(guard_id);
762
35
        self.guards.modify_by_all_ids(guard_id, |guard| match how {
763
6
            Some(external) => guard.record_external_failure(external, now),
764
29
            None => guard.record_failure(now, is_primary),
765
35
        });
766
35
    }
767

            
768
    /// Record that an attempt to use the guard with `guard_id` has
769
    /// just been abandoned, without learning whether it succeeded or failed.
770
352945
    pub(crate) fn record_attempt_abandoned(&mut self, guard_id: &GuardId) {
771
352945
        self.guards
772
352945
            .modify_by_all_ids(guard_id, |guard| guard.note_exploratory_circ(false));
773
352945
    }
774

            
775
    /// Record that an attempt to use the guard with `guard_id` has
776
    /// just failed in a way that we could not definitively attribute to
777
    /// the guard.
778
    pub(crate) fn record_indeterminate_result(&mut self, guard_id: &GuardId) {
779
        self.guards.modify_by_all_ids(guard_id, |guard| {
780
            guard.note_exploratory_circ(false);
781
            guard.record_indeterminate_result();
782
        });
783
    }
784

            
785
    /// Record that a given guard has told us about clock skew.
786
    pub(crate) fn record_skew(&mut self, guard_id: &GuardId, observation: SkewObservation) {
787
        self.guards
788
            .modify_by_all_ids(guard_id, |guard| guard.note_skew(observation));
789
    }
790

            
791
    /// Return an iterator over all stored clock skew observations.
792
    pub(crate) fn skew_observations(&self) -> impl Iterator<Item = &SkewObservation> {
793
        self.guards.values().filter_map(|g| g.skew())
794
    }
795

            
796
    /// Return whether the circuit manager can be allowed to use a
797
    /// circuit with the `guard_id`.
798
    ///
799
    /// Return `Some(bool)` if the circuit is usable, and `None` if we
800
    /// cannot yet be sure.
801
10302
    pub(crate) fn circ_usability_status(
802
10302
        &self,
803
10302
        guard_id: &GuardId,
804
10302
        usage: &GuardUsage,
805
10302
        params: &GuardParams,
806
10302
        now: Instant,
807
10302
    ) -> Option<bool> {
808
        // TODO-SPEC: This isn't what the spec says.  The spec is phrased
809
        // in terms of circuits blocking circuits, whereas this algorithm is
810
        // about guards blocking guards.
811
        //
812
        // Also notably, the spec also says:
813
        //
814
        // * Among guards that do not appear in {CONFIRMED_GUARDS},
815
        // {is_pending}==true guards have higher priority.
816
        // * Among those, the guard with earlier {last_tried_connect} time
817
        // has higher priority.
818
        // * Finally, among guards that do not appear in
819
        // {CONFIRMED_GUARDS} with {is_pending==false}, all have equal
820
        // priority.
821
        //
822
        // I believe this approach is fine too, but we ought to document it.
823

            
824
10302
        if self.guard_is_primary(guard_id) {
825
            // Circuits built to primary guards are always usable immediately.
826
            //
827
            // This has to be a special case, since earlier primary guards
828
            // don't block later ones.
829
10292
            return Some(true);
830
10
        }
831

            
832
        // Assuming that the guard is _not_ primary, then the rule is
833
        // fairly simple: we can use the guard if all the guards we'd
834
        // _rather_ use are either down, or have had their circuit
835
        // attempts pending for too long.
836

            
837
10
        let cutoff = now
838
10
            .checked_sub(params.np_connect_timeout)
839
10
            .expect("Can't subtract connect timeout from now.");
840

            
841
26
        for (src, guard) in self.preference_order() {
842
26
            if guard.guard_id() == guard_id {
843
7
                return Some(true);
844
19
            }
845
19
            if guard.usable() && self.active_filter.permits(guard) && guard.conforms_to_usage(usage)
846
            {
847
19
                match (src, guard.reachable()) {
848
2
                    (_, Reachable::Reachable) => return Some(false),
849
16
                    (_, Reachable::Unreachable) => (),
850
                    (ListKind::Primary, Reachable::Untried | Reachable::Retriable) => {
851
                        return Some(false);
852
                    }
853
                    (_, Reachable::Untried | Reachable::Retriable) => {
854
1
                        if guard.exploratory_attempt_after(cutoff) {
855
1
                            return None;
856
                        }
857
                    }
858
                }
859
            }
860
        }
861

            
862
        // This guard is not even listed.
863
        Some(false)
864
10302
    }
865

            
866
    /// Try to select a guard for a given `usage`.
867
    ///
868
    /// On success, returns the kind of guard that we got, and its filtered
869
    /// representation in a form suitable for use as a first hop.
870
    ///
871
    /// Label the returned guard as having come from `sample_id`.
872
    //
873
    // NOTE (nickm): I wish that we didn't have to take sample_id as an input,
874
    // but the alternative would be storing it as a member of `GuardSet`, which
875
    // makes things very complicated.
876
565994
    pub(crate) fn pick_guard(
877
565994
        &self,
878
565994
        sample_id: &GuardSetSelector,
879
565994
        usage: &GuardUsage,
880
565994
        params: &GuardParams,
881
565994
        now: Instant,
882
565994
    ) -> Result<(ListKind, FirstHop), PickGuardError> {
883
565994
        let (list_kind, id) = self.pick_guard_id(usage, params, now)?;
884
555874
        let first_hop = self
885
555874
            .get(&id)
886
555874
            .expect("Somehow selected a guard we don't know!")
887
555874
            .get_external_rep(sample_id.clone());
888
555874
        let first_hop = self.active_filter.modify_hop(first_hop)?;
889

            
890
555874
        Ok((list_kind, first_hop))
891
565994
    }
892

            
893
    /// Try to select a guard for a given `usage`.
894
    ///
895
    /// On success, returns the kind of guard that we got, and its identity.
896
566074
    fn pick_guard_id(
897
566074
        &self,
898
566074
        usage: &GuardUsage,
899
566074
        params: &GuardParams,
900
566074
        now: Instant,
901
566074
    ) -> Result<(ListKind, GuardId), PickGuardError> {
902
566074
        debug_assert!(!self.primary_guards_invalidated);
903
566074
        let n_options = match usage.kind {
904
10900
            GuardUsageKind::OneHopDirectory => params.dir_parallelism,
905
555174
            GuardUsageKind::Data => params.data_parallelism,
906
        };
907

            
908
        // Counts of how many elements were rejected by which of the filters
909
        // below.
910
        //
911
        // Note that since we use `Iterator::take`, these counts won't cover the
912
        // whole guard sample on the successful case: only in the failing case,
913
        // when we fail to find any candidates.
914
566074
        let mut running = FilterCount::default();
915
566074
        let mut pending = FilterCount::default();
916
566074
        let mut suitable = FilterCount::default();
917
566074
        let mut filtered = FilterCount::default();
918

            
919
566074
        let mut options: Vec<_> = self
920
566074
            .preference_order()
921
            // Discard the guards that are down or unusable, and see if any
922
            // are left.
923
578186
            .filter_cnt(&mut running, |(_, g)| {
924
578186
                g.usable()
925
578186
                    && g.reachable() != Reachable::Unreachable
926
578006
                    && g.ready_for_usage(usage, now)
927
578186
            })
928
            // Now remove those that are excluded because we're already trying
929
            // them on an exploratory basis.
930
578000
            .filter_cnt(&mut pending, |(_, g)| !g.exploratory_circ_pending())
931
            // ...or because they don't support the operation we're
932
            // attempting...
933
577993
            .filter_cnt(&mut suitable, |(_, g)| g.conforms_to_usage(usage))
934
            // ... or because we specifically filtered them out.
935
577729
            .filter_cnt(&mut filtered, |(_, g)| self.active_filter.permits(*g))
936
            // We only consider the first n_options such guards.
937
566074
            .take(n_options)
938
566074
            .collect();
939

            
940
566074
        if options.iter().any(|(src, _)| src.is_primary()) {
941
            // If there are any primary guards, we only consider those.
942
577712
            options.retain(|(src, _)| src.is_primary());
943
10138
        } else {
944
10138
            // If there are no primary guards, parallelism doesn't apply.
945
10138
            options.truncate(1);
946
10138
        }
947

            
948
566074
        match options.choose(&mut rand::rng()) {
949
555953
            Some((src, g)) => Ok((*src, g.guard_id().clone())),
950
            None => {
951
10121
                let retry_at = if running.n_accepted == 0 {
952
10121
                    self.next_retry(usage)
953
                } else {
954
                    None
955
                };
956
10121
                Err(PickGuardError::AllGuardsDown {
957
10121
                    retry_at,
958
10121
                    running,
959
10121
                    pending,
960
10121
                    suitable,
961
10121
                    filtered,
962
10121
                })
963
            }
964
        }
965
566074
    }
966

            
967
    /// Return the guards whose bridge descriptors we should request, given our
968
    /// current configuration and status.
969
    ///
970
    /// (The output of this function is not reasonable unless this is a Bridge
971
    /// sample.)
972
    #[cfg(feature = "bridge-client")]
973
    pub(crate) fn descriptors_to_request(&self, now: Instant, params: &GuardParams) -> Vec<&Guard> {
974
        /// This constant is here to improve our odds that we can get a working
975
        /// bridge if we have any per-circuit filters that would prevent us from
976
        /// using our preferred bridge.
977
        const MINIMUM: usize = 2;
978

            
979
        let maximum = std::cmp::max(params.data_parallelism, MINIMUM);
980
        let data_usage = GuardUsage::default();
981

            
982
        // Here we duplicate some but not all of the restrictions above in
983
        // pick_guard_id.  We skip those restrictions that are specific to only
984
        // certain kinds of circuits, and those that are temporary restrictions
985
        // encouraging us to try more guards.
986
        //
987
        // TODO: we may want to refactor this code and the code in pick_guard_id
988
        // above to share a single function.  Before we do that, however, I want
989
        // to experiment with this logic a bit to make sure that it works and
990
        // doesn't give us surprising results.
991
        self.preference_order()
992
            .filter(|(_, g)| {
993
                g.usable()
994
                    && g.reachable() != Reachable::Unreachable
995
                    && g.ready_for_usage(&data_usage, now)
996
                    && self.active_filter.permits(*g)
997
            })
998
            .take(maximum)
999
            .map(|(_, g)| g)
            .collect()
    }
}
use serde::Serializer;
use tor_persist::JsonValue;
/// State object used to serialize and deserialize a [`GuardSet`].
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub(crate) struct GuardSample<'a> {
    /// Equivalent to `GuardSet.guards.values()`, except in sample order.
    guards: Vec<Cow<'a, Guard>>,
    /// The identities for the confirmed members of `guards`, in confirmed order.
    confirmed: Cow<'a, [GuardId]>,
    /// Other data from the state file that this version of Arti doesn't recognize.
    #[serde(flatten)]
    remaining: HashMap<String, JsonValue>,
}
impl Serialize for GuardSet {
18
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
18
    where
18
        S: Serializer,
    {
18
        GuardSample::from(self).serialize(serializer)
18
    }
}
impl<'a> From<&'a GuardSet> for GuardSample<'a> {
19
    fn from(guards: &'a GuardSet) -> Self {
19
        guards.get_state()
19
    }
}
impl<'a> From<GuardSample<'a>> for GuardSet {
19
    fn from(sample: GuardSample) -> Self {
19
        GuardSet::from_state(sample)
19
    }
}
#[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_time_subtraction)]
    #![allow(clippy::useless_vec)]
    #![allow(clippy::needless_pass_by_value)]
    #![allow(clippy::string_slice)] // See arti#2571
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
    use tor_linkspec::{HasRelayIds, RelayIdType};
    use tor_netdir::NetDir;
    use tor_netdoc::doc::netstatus::RelayWeight;
    use tor_netdoc::types::relay_flags::RelayFlag;
    use web_time_compat::{InstantExt, SystemTimeExt};
    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(RelayFlag::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::get(), &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::get()));
                }
            }
            // Make sure that the sample doesn't expand any further.
            guards.extend_sample_as_needed(SystemTime::get(), &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::get();
        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, std::slice::from_ref(&id1));
        // 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::get();
        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, std::slice::from_ref(&id3));
        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::get();
        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]
    fn sampling_and_usage() {
        let netdir = netdir();
        let params = GuardParams {
            min_filtered_sample_size: 5,
            n_primary: 2,
            ..GuardParams::default()
        };
        let st1 = SystemTime::get();
        let i1 = Instant::get();
        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::get();
        let mut inst = Instant::get();
        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::get(), &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::get())
            .unwrap();
        assert_eq!(kind, ListKind::Primary);
        guards.record_failure(&p_id1, None, Instant::get());
        assert!(!guards.all_primary_guards_are_unreachable());
        // Now let the other one fail.
        let (kind, p_id2) = guards
            .pick_guard_id(&usage, &params, Instant::get())
            .unwrap();
        assert_eq!(kind, ListKind::Primary);
        guards.record_failure(&p_id2, None, Instant::get());
        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::get())
            .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::get(), &params, &netdir);
        guards.select_primary_guards(&params);
        assert_eq!(guards.primary.len(), 2);
        let (_kind, p_id1) = guards
            .pick_guard_id(&usage, &params, Instant::get())
            .unwrap();
        guards.record_success(&p_id1, &params, None, SystemTime::get());
        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::get(), &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::get());
        guards2.record_success(&id2, &params, None, SystemTime::get());
        // Make a non-persistent change in guards2.
        guards2.record_failure(&id2, None, Instant::get());
        // 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::get(), &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);
    }
}