hopr_transport_network/
config.rs

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