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