hopr_transport_network/
config.rs

1use serde::{Deserialize, Serialize};
2use serde_with::{serde_as, DurationSeconds};
3use smart_default::SmartDefault;
4use std::time::Duration;
5use validator::Validate;
6
7/// Network quality threshold since which a node is considered
8/// available enough to be used
9pub const DEFAULT_NETWORK_OFFLINE_QUALITY_THRESHOLD: f64 = 0.0;
10pub const DEFAULT_NETWORK_BAD_QUALITY_THRESHOLD: f64 = 0.1;
11pub const DEFAULT_NETWORK_QUALITY_STEP: f64 = 0.1;
12pub const DEFAULT_NETWORK_QUALITY_AVERAGE_WINDOW_SIZE: u32 = 25;
13pub const DEFAULT_NETWORK_BACKOFF_EXPONENT: f64 = 1.5;
14pub const DEFAULT_NETWORK_BACKOFF_MIN: f64 = 2.0;
15
16pub const DEFAULT_AUTO_PATH_QUALITY_THRESHOLD: f64 = 0.95;
17
18pub const DEFAULT_MAX_FIRST_HOP_LATENCY_THRESHOLD: Duration = Duration::from_millis(100);
19
20/// Configuration for the [`crate::network::Network`] object
21#[serde_as]
22#[derive(Debug, Clone, Serialize, Deserialize, SmartDefault, PartialEq)]
23#[serde(deny_unknown_fields)]
24pub struct NetworkConfig {
25    /// Minimum delay will be multiplied by backoff, it will be half the actual minimum value
26    #[serde_as(as = "DurationSeconds<u64>")]
27    #[serde(default = "duration_1_s")]
28    #[default(duration_1_s())]
29    pub min_delay: Duration,
30
31    /// Maximum delay
32    #[serde_as(as = "DurationSeconds<u64>")]
33    #[serde(default = "duration_5_min")]
34    #[default(duration_5_min())]
35    pub max_delay: Duration,
36
37    #[serde(default = "quality_bad_threshold")]
38    #[default(quality_bad_threshold())]
39    pub quality_bad_threshold: f64,
40
41    #[serde(default = "quality_offline_threshold")]
42    #[default(quality_offline_threshold())]
43    pub quality_offline_threshold: f64,
44
45    #[serde(default = "node_score_auto_path_threshold")]
46    #[default(node_score_auto_path_threshold())]
47    pub node_score_auto_path_threshold: f64,
48
49    #[serde(default = "max_first_hop_latency_threshold")]
50    #[default(max_first_hop_latency_threshold())]
51    pub max_first_hop_latency_threshold: Option<Duration>,
52
53    #[serde(default = "quality_step")]
54    #[default(quality_step())]
55    pub quality_step: f64,
56
57    #[serde(default = "quality_average_window_size")]
58    #[default(quality_average_window_size())]
59    pub quality_avg_window_size: u32,
60
61    #[serde_as(as = "DurationSeconds<u64>")]
62    #[serde(default = "duration_2_min")]
63    #[default(duration_2_min())]
64    pub ignore_timeframe: Duration,
65
66    #[serde(default = "backoff_exponent")]
67    #[default(backoff_exponent())]
68    pub backoff_exponent: f64,
69
70    #[serde(default = "backoff_min")]
71    #[default(backoff_min())]
72    pub backoff_min: f64,
73
74    #[serde(default = "backoff_max")]
75    #[default(backoff_max())]
76    pub backoff_max: f64,
77}
78
79impl Validate for NetworkConfig {
80    fn validate(&self) -> std::result::Result<(), validator::ValidationErrors> {
81        let mut errors = validator::ValidationErrors::new();
82
83        if self.min_delay >= self.max_delay {
84            errors.add(
85                "min_delay and max_delay",
86                validator::ValidationError::new("min_delay must be less than max_delay"),
87            );
88        }
89
90        // #[validate(range(min = 0.0, max = 1.0))]
91        if !(0.0..=1.0).contains(&self.quality_bad_threshold) {
92            errors.add(
93                "quality_bad_threshold",
94                validator::ValidationError::new("quality_bad_threshold must be between 0 and 1"),
95            );
96        }
97
98        if !(0.0..=1.0).contains(&self.node_score_auto_path_threshold) {
99            errors.add(
100                "node_score_auto_path_threshold",
101                validator::ValidationError::new("node_score_auto_path_threshold must be between 0 and 1"),
102            );
103        }
104
105        // #[validate(range(min = 0.0, max = 1.0))]
106        if !(0.0..=1.0).contains(&self.quality_offline_threshold) {
107            errors.add(
108                "quality_offline_threshold",
109                validator::ValidationError::new("quality_offline_threshold must be between 0 and 1"),
110            );
111        }
112
113        if self.quality_bad_threshold < self.quality_offline_threshold {
114            errors.add(
115                "quality_bad_threshold and quality_offline_threshold",
116                validator::ValidationError::new("quality_bad_threshold must be greater than quality_offline_threshold"),
117            );
118        }
119
120        // #[validate(range(min = 0.0, max = 1.0))]
121        if !(0.0..=1.0).contains(&self.quality_step) {
122            errors.add(
123                "quality_step",
124                validator::ValidationError::new("quality_step must be between 0 and 1"),
125            );
126        }
127
128        // #[validate(range(min = 1_u32))]
129        if self.quality_avg_window_size < 1 {
130            errors.add(
131                "quality_avg_window_size",
132                validator::ValidationError::new("quality_avg_window_size must be greater than 0"),
133            );
134        }
135
136        if self.backoff_min >= self.backoff_max {
137            errors.add(
138                "backoff_min and backoff_max",
139                validator::ValidationError::new("backoff_min must be less than backoff_max"),
140            );
141        }
142
143        if errors.is_empty() {
144            Ok(())
145        } else {
146            Err(errors)
147        }
148    }
149}
150
151#[inline]
152fn duration_1_s() -> Duration {
153    Duration::from_secs(1)
154}
155
156#[inline]
157fn duration_5_min() -> Duration {
158    Duration::from_secs(300)
159}
160
161#[inline]
162fn quality_bad_threshold() -> f64 {
163    DEFAULT_NETWORK_BAD_QUALITY_THRESHOLD
164}
165
166#[inline]
167fn quality_offline_threshold() -> f64 {
168    DEFAULT_NETWORK_OFFLINE_QUALITY_THRESHOLD
169}
170
171#[inline]
172fn node_score_auto_path_threshold() -> f64 {
173    DEFAULT_AUTO_PATH_QUALITY_THRESHOLD
174}
175
176#[inline]
177fn max_first_hop_latency_threshold() -> Option<Duration> {
178    Some(DEFAULT_MAX_FIRST_HOP_LATENCY_THRESHOLD)
179}
180
181#[inline]
182fn quality_step() -> f64 {
183    DEFAULT_NETWORK_QUALITY_STEP
184}
185
186#[inline]
187fn quality_average_window_size() -> u32 {
188    DEFAULT_NETWORK_QUALITY_AVERAGE_WINDOW_SIZE
189}
190
191#[inline]
192fn duration_2_min() -> Duration {
193    Duration::from_secs(2 * 60)
194}
195
196#[inline]
197fn backoff_exponent() -> f64 {
198    DEFAULT_NETWORK_BACKOFF_EXPONENT
199}
200
201#[inline]
202fn backoff_min() -> f64 {
203    DEFAULT_NETWORK_BACKOFF_MIN
204}
205
206#[inline]
207fn backoff_max() -> f64 {
208    duration_5_min().as_millis() as f64 / duration_1_s().as_millis() as f64
209}