hopr_transport/path/traits.rs
1use hopr_api::OffchainPublicKey;
2
3use super::errors::Result;
4
5/// A candidate path paired with its accumulated traversal cost and per-path quality metrics.
6///
7/// The `cost` is a multiplicative product of per-edge quality scores in
8/// `(0.0, 1.0]` — higher means better quality.
9///
10/// Aggregate fields are `Option<T>` with per-field `None` semantics documented on each
11/// field. Latency-measured paths are preferred during pruning; paths with `None` latency
12/// fill remaining slots up to the anonymity floor.
13#[derive(Debug, Clone)]
14pub struct PathWithMetrics {
15 /// The path nodes (excluding source): `[intermediates..., dest]`.
16 pub path: Vec<OffchainPublicKey>,
17 /// Accumulated traversal cost.
18 pub cost: f64,
19 /// Sum of per-edge EMA latencies in milliseconds.
20 /// `None` if any edge along the path has no measured latency.
21 pub total_latency_ms: Option<u32>,
22 /// Worst per-edge probe success rate along the path, taken as the minimum of
23 /// the available immediate (1-hop) and intermediate (multi-hop) probe rates per edge.
24 /// `None` if no edge has any probe data.
25 pub min_probe_success_rate: Option<f64>,
26 /// Worst per-edge acknowledgment rate along the path.
27 /// `None` if no edge has sent any messages yet.
28 pub min_ack_rate: Option<f64>,
29 /// Smallest number of single-hop tickets any edge along the path can still fund.
30 ///
31 /// Derived per query from each edge's balance and the current ticket face value — never stored
32 /// on an edge, which would stale the graph whenever the price moves.
33 /// `None` if no edge carries balance data.
34 pub fundable_tickets_floor: Option<u128>,
35}
36
37/// Selects multi-hop paths through the network.
38///
39/// Implementors are responsible for determining how paths are found.
40/// The caller (e.g. [`crate::path::planner::PathPlanner`]) is responsible for caching,
41/// path selection strategy, and validation.
42///
43/// # Cycle-free invariant
44///
45/// Implementations **must** return only cycle-free (simple) paths — no node may
46/// appear more than once in any returned path. Cycles destroy path entropy and
47/// worsen anonymity. The built-in [`crate::path::selector::HoprGraphPathSelector`]
48/// guarantees this by using the `simple_paths` graph algorithm, which by
49/// definition never revisits a node. Alternative implementations must uphold
50/// the same invariant.
51pub trait PathSelector {
52 /// Return **all** candidate paths from `src` to `dest` using `hops` relays.
53 ///
54 /// Each returned [`PathWithMetrics`] contains a path `Vec<OffchainPublicKey>`
55 /// of length `hops + 1` (`[intermediates..., dest]`; `src` excluded) paired
56 /// with its accumulated traversal cost and optional per-path quality aggregates.
57 ///
58 /// Every returned path must be cycle-free (see trait-level docs).
59 ///
60 /// Returns `Err` when no paths can be found.
61 fn select_path(&self, src: OffchainPublicKey, dest: OffchainPublicKey, hops: usize)
62 -> Result<Vec<PathWithMetrics>>;
63}
64
65/// A selector that can run a background path-cache refresh loop.
66///
67/// Implementors pre-warm their internal caches on a periodic schedule,
68/// so that steady-state traffic is always served without a blocking query.
69///
70/// The returned future is `'static` because it is intended to be
71/// spawned as a long-lived background task.
72pub trait BackgroundPathCacheRefreshable: Send + Sync {
73 /// Returns a future that runs the periodic cache-refresh loop.
74 ///
75 /// The future never completes under normal operation.
76 fn run_background_refresh(&self) -> impl std::future::Future<Output = ()> + Send + 'static;
77}