From 6f25dd05af0fadd6bef23cfa30d9a5aefc6234b4 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 2 Dec 2020 14:09:33 -0500 Subject: [PATCH] Use a better calculation for deciding if we have enough paths. --- tor-netdir/src/lib.rs | 41 ++++++++++++++++++++++++--------- tor-netdoc/src/doc/netstatus.rs | 33 +++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/tor-netdir/src/lib.rs b/tor-netdir/src/lib.rs index 3d437343f..69207e66e 100644 --- a/tor-netdir/src/lib.rs +++ b/tor-netdir/src/lib.rs @@ -176,22 +176,41 @@ impl NetDir { pub fn relays(&self) -> impl Iterator> { self.all_relays().filter_map(UncheckedRelay::into_relay) } - /// Return true if there is enough information in this NetDir to build - /// multihop circuits. - fn have_enough_paths(&self) -> bool { - // TODO: Implement the real path-based algorithm. - let mut total_bw = 0_u64; - let mut have_bw = 0_u64; + /// Return the fraction of total bandwidth weight for a given role + /// that we have available information for in this NetDir. + fn frac_for_role(&self, role: WeightRole) -> f64 { + let mut total_weight = 0_u64; + let mut have_weight = 0_u64; + for r in self.all_relays() { - let w = self.weights.weight_rs_for_role(&r.rs, WeightRole::Middle); - total_bw += w; + let w = self.weights.weight_rs_for_role(&r.rs, role); + total_weight += w; if r.is_usable() { - have_bw += w; + have_weight += w } } - // TODO: Do a real calculation here. - have_bw > (total_bw / 2) + (have_weight as f64) / (total_weight as f64) + } + /// Return true if there is enough information in this NetDir to build + /// multihop circuits. + fn have_enough_paths(&self) -> bool { + // If we can build a randomly chosen path with at least this + // probability, we know enough information to participate + // on the network. + let min_pct = self + .consensus + .params() + .get_clamped("min_paths_for_circs_pct", 25, 95) + .unwrap_or(60); + let min_frac_paths = (min_pct as f64) / 100.0; + + // What fraction of paths can we build? + let available = self.frac_for_role(WeightRole::Guard) + * self.frac_for_role(WeightRole::Middle) + * self.frac_for_role(WeightRole::Exit); + + available >= min_frac_paths } /// Chose a relay at random. /// diff --git a/tor-netdoc/src/doc/netstatus.rs b/tor-netdoc/src/doc/netstatus.rs index 6a9acb935..f8064c954 100644 --- a/tor-netdoc/src/doc/netstatus.rs +++ b/tor-netdoc/src/doc/netstatus.rs @@ -136,7 +136,33 @@ impl NetParams { self.params.get(v.as_ref()) } } - +impl NetParams +where + T: Copy + Clone, +{ + /// Return a given network parameter by value, if it is present. + pub fn get_val>(&self, v: A) -> Option { + self.get(v).map(T::clone) + } +} +impl NetParams +where + T: Copy + Clone + PartialOrd, +{ + /// Return a given network parameter by value, if it is + /// present. Clamp it so that it lies within the range `low..=high`. + pub fn get_clamped>(&self, v: A, low: T, high: T) -> Option { + self.get_val(v).map(|x| { + if x < low { + low + } else if x > high { + high + } else { + x + } + }) + } +} /// A list of subprotocol versions that implementors should/must provide. #[allow(dead_code)] #[derive(Debug, Clone)] @@ -477,6 +503,11 @@ impl MDConsensus { pub fn bandwidth_weights(&self) -> &NetParams { &self.footer.weights } + + /// Return the map of network parameters that this consensus advertises. + pub fn params(&self) -> &NetParams { + &self.header.hdr.params + } } decl_keyword! {