hopr_lib/
config.rs

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
25#[inline]
26fn default_network() -> String {
27    "anvil-localhost".to_owned()
28}
29
30#[inline]
31fn just_true() -> bool {
32    true
33}
34
35#[derive(Debug, Clone, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, Validate)]
36#[serde(deny_unknown_fields)]
37pub struct Chain {
38    #[validate(custom(function = "validate_announced"))]
39    #[serde(default = "just_true")]
40    #[default = true]
41    pub announce: bool,
42    #[serde(default = "default_network")]
43    #[default(default_network())]
44    pub network: String,
45    #[serde(default)]
46    pub provider: Option<String>,
47    #[serde(default)]
48    pub max_rpc_requests_per_sec: Option<u32>,
49    #[serde(default)]
50    pub protocols: hopr_chain_api::config::ProtocolsConfig,
51    #[serde(default = "just_true")]
52    #[default = true]
53    pub keep_logs: bool,
54    #[serde(default = "just_true")]
55    #[default = true]
56    pub fast_sync: bool,
57}
58
59#[inline]
60fn default_invalid_address() -> Address {
61    Address::default()
62}
63
64#[inline]
65fn default_safe_transaction_service_provider() -> String {
66    DEFAULT_SAFE_TRANSACTION_SERVICE_PROVIDER.to_owned()
67}
68
69#[serde_as]
70#[derive(Debug, Clone, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, Validate)]
71#[serde(deny_unknown_fields)]
72pub struct SafeModule {
73    #[validate(url)]
74    #[serde(default = "default_safe_transaction_service_provider")]
75    #[default(default_safe_transaction_service_provider())]
76    pub safe_transaction_service_provider: String,
77    #[serde_as(as = "DisplayFromStr")]
78    #[serde(default = "default_invalid_address")]
79    #[default(default_invalid_address())]
80    pub safe_address: Address,
81    #[serde_as(as = "DisplayFromStr")]
82    #[serde(default = "default_invalid_address")]
83    #[default(default_invalid_address())]
84    pub module_address: Address,
85}
86
87#[allow(dead_code)]
88fn validate_directory_exists(s: &str) -> Result<(), ValidationError> {
89    if std::path::Path::new(s).is_dir() {
90        Ok(())
91    } else {
92        Err(ValidationError::new("Invalid directory path specified"))
93    }
94}
95
96#[derive(Debug, Clone, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, Validate)]
97#[serde(deny_unknown_fields)]
98pub struct Db {
99    /// Path to the directory containing the database
100    #[serde(default)]
101    pub data: String,
102    #[serde(default = "just_true")]
103    #[default = true]
104    pub initialize: bool,
105    #[serde(default)]
106    pub force_initialize: bool,
107}
108
109#[derive(Debug, Clone, PartialEq, smart_default::SmartDefault, Serialize, Deserialize, Validate)]
110pub struct HoprLibConfig {
111    /// Configuration related to host specifics
112    #[validate(nested)]
113    #[validate(custom(function = "validate_external_host"))]
114    #[serde(default = "default_host")]
115    #[default(default_host())]
116    pub host: HostConfig,
117    /// Configuration of the underlying database engine
118    #[validate(nested)]
119    #[serde(default)]
120    pub db: Db,
121    /// Configuration of underlying node behavior in the form strategies
122    ///
123    /// Strategies represent automatically executable behavior performed by
124    /// the node given pre-configured triggers.
125    #[validate(nested)]
126    #[serde(default = "hopr_strategy::hopr_default_strategies")]
127    #[default(hopr_strategy::hopr_default_strategies())]
128    pub strategy: StrategyConfig,
129    /// Configuration of the protocol heartbeat mechanism
130    #[validate(nested)]
131    #[serde(default)]
132    pub probe: ProbeConfig,
133    /// Configuration of network properties
134    #[validate(nested)]
135    #[serde(default)]
136    pub network_options: NetworkConfig,
137    /// Configuration specific to transport mechanics
138    #[validate(nested)]
139    #[serde(default)]
140    pub transport: TransportConfig,
141    /// Configuration specific to protocol execution on the p2p layer
142    #[validate(nested)]
143    #[serde(default)]
144    pub protocol: ProtocolConfig,
145    /// Configuration specific to Session management.
146    #[validate(nested)]
147    #[serde(default)]
148    pub session: SessionGlobalConfig,
149    /// Blockchain-specific configuration
150    #[validate(nested)]
151    #[serde(default)]
152    pub chain: Chain,
153    /// Configuration of the `Safe` mechanism
154    #[validate(nested)]
155    #[serde(default)]
156    pub safe_module: SafeModule,
157}
158
159// NOTE: this intentionally does not validate (0.0.0.0) to force user to specify
160// their external IP.
161#[inline]
162fn default_host() -> HostConfig {
163    HostConfig {
164        address: HostType::IPv4(DEFAULT_HOST.to_owned()),
165        port: DEFAULT_PORT,
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172
173    #[test]
174    fn test_config_should_be_serializable_using_serde() -> Result<(), Box<dyn std::error::Error>> {
175        let cfg = HoprLibConfig::default();
176
177        let yaml = serde_yaml::to_string(&cfg)?;
178        let cfg_after_serde: HoprLibConfig = serde_yaml::from_str(&yaml)?;
179        assert_eq!(cfg, cfg_after_serde);
180
181        Ok(())
182    }
183}