Rename Guard=>FirstHop, GuardId=>FirstHopId

This is preparation for having separate GuardId and FirstHopId types
that distinguish which back-end they index.
This commit is contained in:
Nick Mathewson 2022-03-29 09:05:39 -04:00
parent 9803b645c0
commit 13af6134f6
8 changed files with 91 additions and 77 deletions

View File

@ -87,7 +87,7 @@ use crate::isolation::StreamIsolation;
use crate::preemptive::PreemptiveCircuitPredictor;
use usage::TargetCircUsage;
pub use tor_guardmgr::{ExternalActivity, GuardId};
pub use tor_guardmgr::{ExternalActivity, FirstHopId};
use tor_persist::{FsStateMgr, StateMgr};
use tor_rtcompat::scheduler::{TaskHandle, TaskSchedule};
@ -695,7 +695,7 @@ impl<R: Runtime> CircMgr<R> {
/// Record that a failure occurred on a circuit with a given guard, in a way
/// that makes us unwilling to use that guard for future circuits.
pub fn note_external_failure(&self, id: &GuardId, external_failure: ExternalActivity) {
pub fn note_external_failure(&self, id: &FirstHopId, external_failure: ExternalActivity) {
self.mgr
.peek_builder()
.guardmgr()
@ -704,7 +704,7 @@ impl<R: Runtime> CircMgr<R> {
/// Record that a success occurred on a circuit with a given guard, in a way
/// that makes us possibly willing to use that guard for future circuits.
pub fn note_external_success(&self, id: &GuardId, external_activity: ExternalActivity) {
pub fn note_external_success(&self, id: &FirstHopId, external_activity: ExternalActivity) {
self.mgr
.peek_builder()
.guardmgr()

View File

@ -981,7 +981,7 @@ impl<R: Runtime> DirMgr<R> {
/// Record that a problem has occurred because of a failure in an answer from `source`.
fn note_cache_error(&self, source: &tor_dirclient::SourceInfo, problem: &Error) {
use tor_circmgr::{ExternalActivity, GuardId};
use tor_circmgr::{ExternalActivity, FirstHopId};
if !problem.indicates_cache_failure() {
return;
@ -989,7 +989,7 @@ impl<R: Runtime> DirMgr<R> {
if let Some(circmgr) = &self.circmgr {
info!("Marking {:?} as failed: {}", source, problem);
let guard_id = GuardId::from_chan_target(source.cache_id());
let guard_id = FirstHopId::from_chan_target(source.cache_id());
circmgr.note_external_failure(&guard_id, ExternalActivity::DirCache);
circmgr.retire_circ(source.unique_circ_id());
}
@ -997,11 +997,11 @@ impl<R: Runtime> DirMgr<R> {
/// Record that `source` has successfully given us some directory info.
fn note_cache_success(&self, source: &tor_dirclient::SourceInfo) {
use tor_circmgr::{ExternalActivity, GuardId};
use tor_circmgr::{ExternalActivity, FirstHopId};
if let Some(circmgr) = &self.circmgr {
trace!("Marking {:?} as successful", source);
let guard_id = GuardId::from_chan_target(source.cache_id());
let guard_id = FirstHopId::from_chan_target(source.cache_id());
circmgr.note_external_success(&guard_id, ExternalActivity::DirCache);
}
}

View File

@ -51,9 +51,9 @@ impl FallbackDir {
}
/// Return a copy of this FallbackDir as a [`Guard`](crate::Guard)
pub fn as_guard(&self) -> crate::Guard {
crate::Guard {
id: crate::GuardId::from_chan_target(self),
pub fn as_guard(&self) -> crate::FirstHop {
crate::FirstHop {
id: crate::FirstHopId::from_chan_target(self),
orports: self.orports.clone(),
}
}

View File

@ -4,7 +4,7 @@ use rand::seq::IteratorRandom;
use std::time::Instant;
use super::{FallbackDir, Status};
use crate::{GuardId, PickGuardError};
use crate::{FirstHopId, PickGuardError};
use serde::Deserialize;
/// A list of fallback directories.
@ -62,7 +62,7 @@ pub(crate) struct FallbackState {
#[derive(Debug, Clone)]
pub(super) struct Entry {
/// The inner fallback directory.
pub(super) fallback: crate::Guard,
pub(super) fallback: crate::FirstHop,
/// The status for the fallback directory.
pub(super) status: Status,
}
@ -77,7 +77,7 @@ impl From<FallbackDir> for Entry {
impl Entry {
/// Return the identity for this fallback entry.
fn id(&self) -> &GuardId {
fn id(&self) -> &FirstHopId {
self.fallback.id()
}
}
@ -97,7 +97,7 @@ impl FallbackState {
&self,
rng: &mut R,
now: Instant,
) -> Result<&crate::Guard, PickGuardError> {
) -> Result<&crate::FirstHop, PickGuardError> {
if self.fallbacks.is_empty() {
return Err(PickGuardError::NoCandidatesAvailable);
}
@ -123,7 +123,7 @@ impl FallbackState {
}
/// Return a mutable reference to the entry whose identity is `id`, if there is one.
fn get_mut(&mut self, id: &GuardId) -> Option<&mut Entry> {
fn get_mut(&mut self, id: &FirstHopId) -> Option<&mut Entry> {
match self.fallbacks.binary_search_by(|e| e.id().cmp(id)) {
Ok(idx) => Some(&mut self.fallbacks[idx]),
Err(_) => None,
@ -135,7 +135,7 @@ impl FallbackState {
///
/// Be aware that for fallbacks, we only count a successful directory
/// operation as a success: a circuit success is not enough.
pub(crate) fn note_success(&mut self, id: &GuardId) {
pub(crate) fn note_success(&mut self, id: &FirstHopId) {
if let Some(entry) = self.get_mut(id) {
entry.status.note_success();
}
@ -143,7 +143,7 @@ impl FallbackState {
/// Record that a failure has occurred for the fallback with the given
/// identity.
pub(crate) fn note_failure(&mut self, id: &GuardId, now: Instant) {
pub(crate) fn note_failure(&mut self, id: &FirstHopId, now: Instant) {
if let Some(entry) = self.get_mut(id) {
entry.status.note_failure(now);
}
@ -197,7 +197,7 @@ mod test {
// fabricate some fallbacks.
let fbs = vec![rand_fb(), rand_fb(), rand_fb(), rand_fb()];
let fb_other = rand_fb();
let id_other = GuardId::from_chan_target(&fb_other);
let id_other = FirstHopId::from_chan_target(&fb_other);
// basic case: construct a set
let list: FallbackList = fbs.clone().into();
@ -213,7 +213,7 @@ mod test {
// use the constructed set a little.
for fb in fbs.iter() {
let id = GuardId::from_chan_target(fb);
let id = FirstHopId::from_chan_target(fb);
assert_eq!(set.get_mut(&id).unwrap().id(), &id);
}
assert!(set.get_mut(&id_other).is_none());
@ -246,7 +246,7 @@ mod test {
let mut rng = rand::thread_rng();
let now = Instant::now();
fn lookup_idx(set: &FallbackState, id: &GuardId) -> Option<usize> {
fn lookup_idx(set: &FallbackState, id: &FirstHopId) -> Option<usize> {
set.fallbacks.binary_search_by(|ent| ent.id().cmp(id)).ok()
}
// Basic case: everybody is up.
@ -333,7 +333,7 @@ mod test {
let mut fbs2: Vec<_> = fbs
.into_iter()
// (Remove the fallback with id==ids[2])
.filter(|fb| GuardId::from_chan_target(fb) != ids[2])
.filter(|fb| FirstHopId::from_chan_target(fb) != ids[2])
.collect();
// add 2 new ones.
let fbs_new = [rand_fb(), rand_fb(), rand_fb()];
@ -352,7 +352,7 @@ mod test {
// Make sure that the new fbs are there.
for new_fb in fbs_new {
assert!(set2
.get_mut(&GuardId::from_chan_target(&new_fb))
.get_mut(&FirstHopId::from_chan_target(&new_fb))
.unwrap()
.status
.usable_at(now));

View File

@ -13,7 +13,7 @@ use std::time::{Duration, Instant, SystemTime};
use tracing::{trace, warn};
use crate::util::randomize_time;
use crate::{GuardId, GuardParams, GuardRestriction, GuardUsage};
use crate::{FirstHopId, GuardParams, GuardRestriction, GuardUsage};
use tor_persist::{Futureproof, JsonValue};
/// Tri-state to represent whether a guard is believed to be reachable or not.
@ -83,7 +83,7 @@ impl CrateId {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub(crate) struct Guard {
/// The identity keys for this guard.
id: GuardId, // TODO: Maybe refactor this out as redundant someday.
id: FirstHopId, // TODO: Maybe refactor this out as redundant someday.
/// The most recently seen addresses for making OR connections to this
/// guard.
@ -195,14 +195,14 @@ impl Guard {
);
Self::new(
GuardId::from_chan_target(relay),
FirstHopId::from_chan_target(relay),
relay.addrs().into(),
added_at,
)
}
/// Return a new, manually constructed [`Guard`].
fn new(id: GuardId, orports: Vec<SocketAddr>, added_at: SystemTime) -> Self {
fn new(id: FirstHopId, orports: Vec<SocketAddr>, added_at: SystemTime) -> Self {
Guard {
id,
orports,
@ -225,7 +225,7 @@ impl Guard {
}
/// Return the identity of this Guard.
pub(crate) fn guard_id(&self) -> &GuardId {
pub(crate) fn guard_id(&self) -> &FirstHopId {
&self.id
}
@ -574,8 +574,8 @@ impl Guard {
}
/// Return a [`crate::Guard`] object to represent this guard.
pub(crate) fn get_external_rep(&self) -> crate::Guard {
crate::Guard {
pub(crate) fn get_external_rep(&self) -> crate::FirstHop {
crate::FirstHop {
id: self.id.clone(),
orports: self.orports.clone(),
}
@ -687,8 +687,8 @@ mod test {
assert_eq!(Some(id.version.as_ref()), option_env!("CARGO_PKG_VERSION"));
}
fn basic_id() -> GuardId {
GuardId::new([13; 32].into(), [37; 20].into())
fn basic_id() -> FirstHopId {
FirstHopId::new([13; 32].into(), [37; 20].into())
}
fn basic_guard() -> Guard {
let id = basic_id();
@ -919,7 +919,7 @@ mod test {
// Now try a guard that isn't in the netdir.
let guard255 = Guard::new(
GuardId::new([255; 32].into(), [255; 20].into()),
FirstHopId::new([255; 32].into(), [255; 20].into()),
vec![],
now,
);
@ -960,7 +960,7 @@ mod test {
// Try a guard that isn't in the netdir at all.
let mut guard255 = Guard::new(
GuardId::new([255; 32].into(), [255; 20].into()),
FirstHopId::new([255; 32].into(), [255; 20].into()),
vec!["8.8.8.8:53".parse().unwrap()],
now,
);
@ -974,7 +974,11 @@ mod test {
assert!(!guard255.orports.is_empty());
// Try a guard that is in netdir, but not netdir2.
let mut guard22 = Guard::new(GuardId::new([22; 32].into(), [22; 20].into()), vec![], now);
let mut guard22 = Guard::new(
FirstHopId::new([22; 32].into(), [22; 20].into()),
vec![],
now,
);
let relay22 = guard22.id.get_relay(&netdir).unwrap();
assert_eq!(guard22.listed_in(&netdir), Some(true));
guard22.update_from_netdir(&netdir);
@ -990,7 +994,11 @@ mod test {
assert!(!guard22.microdescriptor_missing);
// Now see what happens for a guard that's in the consensus, but missing an MD.
let mut guard23 = Guard::new(GuardId::new([23; 32].into(), [23; 20].into()), vec![], now);
let mut guard23 = Guard::new(
FirstHopId::new([23; 32].into(), [23; 20].into()),
vec![],
now,
);
assert_eq!(guard23.listed_in(&netdir2), Some(true));
assert_eq!(guard23.listed_in(&netdir3), None);
guard23.update_from_netdir(&netdir3);

View File

@ -459,7 +459,7 @@ impl<R: Runtime> GuardMgr<R> {
&self,
usage: GuardUsage,
netdir: Option<&NetDir>,
) -> Result<(Guard, GuardMonitor, GuardUsable), PickGuardError> {
) -> Result<(FirstHop, GuardMonitor, GuardUsable), PickGuardError> {
let now = self.runtime.now();
let wallclock = self.runtime.wallclock();
@ -520,7 +520,7 @@ impl<R: Runtime> GuardMgr<R> {
/// Record that _after_ we built a circuit with `guard`,something described
/// in `external_failure` went wrong with it.
pub fn note_external_failure(&self, guard: &GuardId, external_failure: ExternalActivity) {
pub fn note_external_failure(&self, guard: &FirstHopId, external_failure: ExternalActivity) {
let now = self.runtime.now();
let mut inner = self.inner.lock().expect("Poisoned lock");
@ -536,7 +536,7 @@ impl<R: Runtime> GuardMgr<R> {
/// Record that _after_ we built a circuit with `guard`, some activity described in `external_activity` was successful with it.
///
pub fn note_external_success(&self, guard: &GuardId, external_activity: ExternalActivity) {
pub fn note_external_success(&self, guard: &FirstHopId, external_activity: ExternalActivity) {
let mut inner = self.inner.lock().expect("Poisoned lock");
if external_activity == ExternalActivity::DirCache {
@ -841,7 +841,7 @@ impl GuardMgrInner {
netdir: Option<&NetDir>,
now: Instant,
wallclock: SystemTime,
) -> Result<(sample::ListKind, Guard), PickGuardError> {
) -> Result<(sample::ListKind, FirstHop), PickGuardError> {
// Try to find a guard.
let first_error = match self.select_guard_once(usage) {
Ok(res1) => return Ok(res1),
@ -886,7 +886,7 @@ impl GuardMgrInner {
fn select_guard_once(
&self,
usage: &GuardUsage,
) -> Result<(sample::ListKind, Guard), PickGuardError> {
) -> Result<(sample::ListKind, FirstHop), PickGuardError> {
let (source, id) = self
.guards
.active_guards()
@ -905,7 +905,10 @@ impl GuardMgrInner {
///
/// Called when we have no guard information to use. Return values are as
/// for [`select_guard()`]
fn select_fallback(&self, now: Instant) -> Result<(sample::ListKind, Guard), PickGuardError> {
fn select_fallback(
&self,
now: Instant,
) -> Result<(sample::ListKind, FirstHop), PickGuardError> {
let fallback = self.fallbacks.choose(&mut rand::thread_rng(), now)?;
Ok((sample::ListKind::Fallback, fallback.clone()))
}
@ -1003,19 +1006,20 @@ impl TryFrom<&NetParameters> for GuardParams {
}
}
/// A unique cryptographic identifier for a selected guard.
/// A unique cryptographic identifier for a selected guard or fallback
/// directory.
///
/// (This is implemented internally using both of the guard's Ed25519
/// and RSA identities.)
/// (This is implemented internally using both of the guard's Ed25519 and RSA
/// identities.)
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct GuardId {
pub struct FirstHopId {
/// Ed25519 identity key for a guard
ed25519: pk::ed25519::Ed25519Identity,
/// RSA identity fingerprint for a guard
rsa: pk::rsa::RsaIdentity,
}
impl GuardId {
impl FirstHopId {
/// Return a new, manually constructed GuardId
fn new(ed25519: pk::ed25519::Ed25519Identity, rsa: pk::rsa::RsaIdentity) -> Self {
Self { ed25519, rsa }
@ -1023,7 +1027,7 @@ impl GuardId {
/// Extract a GuardId from a ChanTarget object.
pub fn from_chan_target<T: tor_linkspec::ChanTarget>(target: &T) -> Self {
GuardId::new(*target.ed_identity(), *target.rsa_identity())
FirstHopId::new(*target.ed_identity(), *target.rsa_identity())
}
/// Return the relay in `netdir` that corresponds to this ID, if there
@ -1033,18 +1037,18 @@ impl GuardId {
}
}
/// Representation of a guard, as returned by [`GuardMgr::select_guard()`].
/// Representation of a guard or fallback, as returned by [`GuardMgr::select_guard()`].
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Guard {
pub struct FirstHop {
/// The guard's identities
id: GuardId,
id: FirstHopId,
/// The addresses at which the guard can be contacted.
orports: Vec<SocketAddr>,
}
impl Guard {
impl FirstHop {
/// Return the identities of this guard.
pub fn id(&self) -> &GuardId {
pub fn id(&self) -> &FirstHopId {
&self.id
}
/// Look up this guard in `netdir`.
@ -1054,7 +1058,7 @@ impl Guard {
}
// This is somewhat redundant with the implementation in crate::guard::Guard.
impl tor_linkspec::ChanTarget for Guard {
impl tor_linkspec::ChanTarget for FirstHop {
fn addrs(&self) -> &[SocketAddr] {
&self.orports[..]
}

View File

@ -7,7 +7,7 @@
//! then the circuit manager can't know whether to use a circuit built
//! through that guard until the guard manager tells it. This is
//! handled via [`GuardUsable`].
use crate::{daemon, sample::ListKind, GuardId};
use crate::{daemon, sample::ListKind, FirstHopId};
use educe::Educe;
use futures::{
@ -263,7 +263,7 @@ impl RequestId {
#[derive(Debug)]
pub(crate) struct PendingRequest {
/// Identity of the guard that we gave out.
guard_id: GuardId,
guard_id: FirstHopId,
/// The usage for which this guard was requested.
///
/// We need this information because, if we find that a better guard
@ -292,7 +292,7 @@ pub(crate) struct PendingRequest {
impl PendingRequest {
/// Create a new PendingRequest.
pub(crate) fn new(
guard_id: GuardId,
guard_id: FirstHopId,
source: ListKind,
usage: crate::GuardUsage,
usable: Option<oneshot::Sender<bool>>,
@ -309,7 +309,7 @@ impl PendingRequest {
}
/// Return the Id of the guard we gave out.
pub(crate) fn guard_id(&self) -> &GuardId {
pub(crate) fn guard_id(&self) -> &FirstHopId {
&self.guard_id
}

View File

@ -3,7 +3,9 @@
use crate::filter::GuardFilter;
use crate::guard::{Guard, NewlyConfirmed, Reachable};
use crate::{ExternalActivity, GuardId, GuardParams, GuardUsage, GuardUsageKind, PickGuardError};
use crate::{
ExternalActivity, FirstHopId, GuardParams, GuardUsage, GuardUsageKind, PickGuardError,
};
use tor_netdir::{NetDir, Relay};
use itertools::Itertools;
@ -44,22 +46,22 @@ use tracing::{debug, info};
#[serde(from = "GuardSample")]
pub(crate) struct GuardSet {
/// Map from identities to guards, for every guard in this sample.
guards: HashMap<GuardId, Guard>,
guards: HashMap<FirstHopId, Guard>,
/// Identities of all the guards in the sample, in sample order.
///
/// This contains the same elements as `self.guards.keys()`, and
/// only exists to define an ordering on the guards.
sample: Vec<GuardId>,
sample: Vec<FirstHopId>,
/// Identities of all the confirmed guards in the sample, in
/// confirmed order.
///
/// This contains a subset of the values in `self.guards.keys()`.
confirmed: Vec<GuardId>,
confirmed: Vec<FirstHopId>,
/// Identities of all the primary guards, in preference order
/// (from best to worst).
///
/// This contains a subset of the values in `self.guards.keys()`.
primary: Vec<GuardId>,
primary: Vec<FirstHopId>,
/// Currently active filter that restricts which guards we can use.
///
/// Note that all of the lists above (with the exception of `primary`)
@ -160,7 +162,7 @@ impl GuardSet {
}
/// Return the guard whose id is `id`, if any.
pub(crate) fn get(&self, id: &GuardId) -> Option<&Guard> {
pub(crate) fn get(&self, id: &FirstHopId) -> Option<&Guard> {
self.guards.get(id)
}
@ -256,7 +258,7 @@ impl GuardSet {
fn contains_relay(&self, relay: &Relay<'_>) -> bool {
// Note: Could implement Borrow instead, but I don't think it'll
// matter.
let id = GuardId::from_chan_target(relay);
let id = FirstHopId::from_chan_target(relay);
self.guards.contains_key(&id)
}
@ -389,7 +391,7 @@ impl GuardSet {
///
/// Does nothing if it is already a guard.
fn add_guard(&mut self, relay: &Relay<'_>, now: SystemTime, params: &GuardParams) {
let id = GuardId::from_chan_target(relay);
let id = FirstHopId::from_chan_target(relay);
if self.guards.contains_key(&id) {
return;
}
@ -500,7 +502,7 @@ impl GuardSet {
/// Return an iterator over the Id for every Guard in the sample that
/// is not known to be Unreachable.
fn reachable_sample_ids(&self) -> impl Iterator<Item = &GuardId> {
fn reachable_sample_ids(&self) -> impl Iterator<Item = &FirstHopId> {
self.sample.iter().filter(move |id| {
let g = self.guards.get(id).expect("Inconsistent guard state");
g.reachable() != Reachable::Unreachable
@ -516,7 +518,7 @@ impl GuardSet {
/// Note that this function will return guards that are not
/// accepted by the current active filter: the caller must apply
/// that filter if appropriate.
fn preference_order_ids(&self) -> impl Iterator<Item = (ListKind, &GuardId)> {
fn preference_order_ids(&self) -> impl Iterator<Item = (ListKind, &FirstHopId)> {
self.primary
.iter()
.map(|id| (ListKind::Primary, id))
@ -532,7 +534,7 @@ impl GuardSet {
}
/// Return true if `guard_id` is the identity for a primary guard.
fn guard_is_primary(&self, guard_id: &GuardId) -> bool {
fn guard_is_primary(&self, guard_id: &FirstHopId) -> bool {
// This is O(n), but the list is short.
self.primary.contains(guard_id)
}
@ -577,7 +579,7 @@ impl GuardSet {
/// Record that an attempt has begun to use the guard with
/// `guard_id`.
pub(crate) fn record_attempt(&mut self, guard_id: &GuardId, now: Instant) {
pub(crate) fn record_attempt(&mut self, guard_id: &FirstHopId, now: Instant) {
let is_primary = self.guard_is_primary(guard_id);
if let Some(guard) = self.guards.get_mut(guard_id) {
guard.record_attempt(now);
@ -592,7 +594,7 @@ impl GuardSet {
/// just succeeded.
pub(crate) fn record_success(
&mut self,
guard_id: &GuardId,
guard_id: &FirstHopId,
params: &GuardParams,
now: SystemTime,
) {
@ -614,7 +616,7 @@ impl GuardSet {
/// of the crate.
pub(crate) fn record_failure(
&mut self,
guard_id: &GuardId,
guard_id: &FirstHopId,
how: Option<ExternalActivity>,
now: Instant,
) {
@ -633,7 +635,7 @@ impl GuardSet {
/// Record that an attempt to use the guard with `guard_id` has
/// just been abandoned, without learning whether it succeeded or failed.
pub(crate) fn record_attempt_abandoned(&mut self, guard_id: &GuardId) {
pub(crate) fn record_attempt_abandoned(&mut self, guard_id: &FirstHopId) {
if let Some(guard) = self.guards.get_mut(guard_id) {
guard.note_exploratory_circ(false);
}
@ -642,7 +644,7 @@ impl GuardSet {
/// Record that an attempt to use the guard with `guard_id` has
/// just failed in a way that we could not definitively attribute to
/// the guard.
pub(crate) fn record_indeterminate_result(&mut self, guard_id: &GuardId) {
pub(crate) fn record_indeterminate_result(&mut self, guard_id: &FirstHopId) {
if let Some(guard) = self.guards.get_mut(guard_id) {
guard.note_exploratory_circ(false);
guard.record_indeterminate_result();
@ -656,7 +658,7 @@ impl GuardSet {
/// cannot yet be sure.
pub(crate) fn circ_usability_status(
&self,
guard_id: &GuardId,
guard_id: &FirstHopId,
usage: &GuardUsage,
params: &GuardParams,
now: Instant,
@ -722,7 +724,7 @@ impl GuardSet {
&self,
usage: &GuardUsage,
params: &GuardParams,
) -> Result<(ListKind, GuardId), PickGuardError> {
) -> Result<(ListKind, FirstHopId), PickGuardError> {
debug_assert!(!self.primary_guards_invalidated);
let n_options = match usage.kind {
GuardUsageKind::OneHopDirectory => params.dir_parallelism,
@ -775,7 +777,7 @@ 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, Vec<GuardId>>,
confirmed: Cow<'a, Vec<FirstHopId>>,
/// Other data from the state file that this version of Arti doesn't recognize.
#[serde(flatten)]
remaining: HashMap<String, JsonValue>,
@ -858,7 +860,7 @@ mod test {
..GuardParams::default()
};
let mut samples: Vec<HashSet<GuardId>> = Vec::new();
let mut samples: Vec<HashSet<FirstHopId>> = Vec::new();
for _ in 0..3 {
let mut guards = GuardSet::default();
guards.extend_sample_as_needed(SystemTime::now(), &params, &netdir);