1use hopr_primitive_types::prelude::*;
2pub use hopr_strategy::StrategyConfig;
3use hopr_transport::config::SessionGlobalConfig;
4pub use hopr_transport::config::{
5 HostConfig, HostType, NetworkConfig, ProbeConfig, ProtocolConfig, TransportConfig, validate_external_host,
6};
7use serde::{Deserialize, Serialize};
8use serde_with::{DisplayFromStr, serde_as};
9use validator::{Validate, ValidationError};
10
11pub const DEFAULT_SAFE_TRANSACTION_SERVICE_PROVIDER: &str = "https://safe-transaction.prod.hoprtech.net/";
12pub const DEFAULT_HOST: &str = "0.0.0.0";
13pub const DEFAULT_PORT: u16 = 9091;
14
15fn validate_announced(v: &bool) -> Result<(), ValidationError> {
16 if *v {
17 Ok(())
18 } else {
19 Err(ValidationError::new(
20 "Announce option should be turned ON in 2.*, only public nodes are supported",
21 ))
22 }
23}
24
25fn validate_logs_snapshot_url(url: &&String) -> Result<(), ValidationError> {
26 if url.is_empty() {
27 return Err(ValidationError::new("Logs snapshot URL must not be empty"));
28 }
29
30 if !url.starts_with("http://") && !url.starts_with("https://") && !url.starts_with("file://") {
32 return Err(ValidationError::new(
33 "Logs snapshot URL must be a valid HTTP, HTTPS, or file:// URL",
34 ));
35 }
36
37 if !url.ends_with(".tar.xz") {
39 return Err(ValidationError::new("Logs snapshot URL must point to a .tar.xz file"));
40 }
41
42 Ok(())
43}
44
45#[inline]
46fn default_network() -> String {
47 "anvil-localhost".to_owned()
48}
49
50#[inline]
51fn just_true() -> bool {
52 true
53}
54
55#[inline]
56fn just_false() -> bool {
57 false
58}
59
60#[derive(Debug, Clone, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, Validate)]
61#[serde(deny_unknown_fields)]
62pub struct Chain {
63 #[validate(custom(function = "validate_announced"))]
64 #[serde(default = "just_true")]
65 #[default = true]
66 pub announce: bool,
67 #[serde(default = "default_network")]
68 #[default(default_network())]
69 pub network: String,
70 #[serde(default)]
71 pub provider: Option<String>,
72 #[serde(default)]
73 pub max_rpc_requests_per_sec: Option<u32>,
74 #[serde(default)]
75 pub protocols: hopr_chain_api::config::ProtocolsConfig,
76 #[serde(default = "just_true")]
77 #[default = true]
78 pub keep_logs: bool,
79 #[serde(default = "just_true")]
80 #[default = true]
81 pub fast_sync: bool,
82 #[serde(default = "just_false")]
83 #[default = false]
84 pub enable_logs_snapshot: bool,
85 #[validate(custom(function = "validate_logs_snapshot_url"))]
86 #[serde(default)]
87 pub logs_snapshot_url: Option<String>,
88}
89
90#[inline]
91fn default_invalid_address() -> Address {
92 Address::default()
93}
94
95#[inline]
96fn default_safe_transaction_service_provider() -> String {
97 DEFAULT_SAFE_TRANSACTION_SERVICE_PROVIDER.to_owned()
98}
99
100#[serde_as]
101#[derive(Debug, Clone, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, Validate)]
102#[serde(deny_unknown_fields)]
103pub struct SafeModule {
104 #[validate(url)]
105 #[serde(default = "default_safe_transaction_service_provider")]
106 #[default(default_safe_transaction_service_provider())]
107 pub safe_transaction_service_provider: String,
108 #[serde_as(as = "DisplayFromStr")]
109 #[serde(default = "default_invalid_address")]
110 #[default(default_invalid_address())]
111 pub safe_address: Address,
112 #[serde_as(as = "DisplayFromStr")]
113 #[serde(default = "default_invalid_address")]
114 #[default(default_invalid_address())]
115 pub module_address: Address,
116}
117
118#[allow(dead_code)]
119fn validate_directory_exists(s: &str) -> Result<(), ValidationError> {
120 if std::path::Path::new(s).is_dir() {
121 Ok(())
122 } else {
123 Err(ValidationError::new("Invalid directory path specified"))
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, Validate)]
128#[serde(deny_unknown_fields)]
129pub struct Db {
130 #[serde(default)]
132 pub data: String,
133 #[serde(default = "just_true")]
134 #[default = true]
135 pub initialize: bool,
136 #[serde(default)]
137 pub force_initialize: bool,
138}
139
140#[derive(Debug, Clone, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, Validate)]
141pub struct HoprLibConfig {
142 #[validate(nested)]
144 #[validate(custom(function = "validate_external_host"))]
145 #[serde(default = "default_host")]
146 #[default(default_host())]
147 pub host: HostConfig,
148 #[validate(nested)]
150 #[serde(default)]
151 pub db: Db,
152 #[validate(nested)]
157 #[serde(default = "hopr_strategy::hopr_default_strategies")]
158 #[default(hopr_strategy::hopr_default_strategies())]
159 pub strategy: StrategyConfig,
160 #[validate(nested)]
162 #[serde(default)]
163 pub probe: ProbeConfig,
164 #[validate(nested)]
166 #[serde(default)]
167 pub network_options: NetworkConfig,
168 #[validate(nested)]
170 #[serde(default)]
171 pub transport: TransportConfig,
172 #[validate(nested)]
174 #[serde(default)]
175 pub protocol: ProtocolConfig,
176 #[validate(nested)]
178 #[serde(default)]
179 pub session: SessionGlobalConfig,
180 #[validate(nested)]
182 #[serde(default)]
183 pub chain: Chain,
184 #[validate(nested)]
186 #[serde(default)]
187 pub safe_module: SafeModule,
188}
189
190#[inline]
193fn default_host() -> HostConfig {
194 HostConfig {
195 address: HostType::IPv4(DEFAULT_HOST.to_owned()),
196 port: DEFAULT_PORT,
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn test_config_should_be_serializable_using_serde() -> Result<(), Box<dyn std::error::Error>> {
206 let cfg = HoprLibConfig::default();
207
208 let yaml = serde_yaml::to_string(&cfg)?;
209 let cfg_after_serde: HoprLibConfig = serde_yaml::from_str(&yaml)?;
210 assert_eq!(cfg, cfg_after_serde);
211
212 Ok(())
213 }
214}