rings_node/native/
config.rs1use std::env;
2use std::fs;
3use std::io;
4use std::path::PathBuf;
5
6use serde::Deserialize;
7use serde::Serialize;
8
9use crate::error::Error;
10use crate::error::Result;
11use crate::prelude::rings_core::ecc::SecretKey;
12use crate::prelude::SessionSk;
13use crate::processor::ProcessorConfig;
14use crate::processor::ProcessorConfigSerialized;
15use crate::util::ensure_parent_dir;
16use crate::util::expand_home;
17
18lazy_static::lazy_static! {
19 static ref DEFAULT_DATA_STORAGE_CONFIG: StorageConfig = StorageConfig {
20 path: get_storage_location(".rings", "data"),
21 capacity: DEFAULT_STORAGE_CAPACITY,
22 };
23 static ref DEFAULT_MEASURE_STORAGE_CONFIG: StorageConfig = StorageConfig {
24 path: get_storage_location(".rings", "measure"),
25 capacity: DEFAULT_STORAGE_CAPACITY,
26 };
27}
28
29pub const DEFAULT_NETWORK_ID: u32 = 1;
30pub const DEFAULT_INTERNAL_API_PORT: u16 = 50000;
31pub const DEFAULT_EXTERNAL_API_ADDR: &str = "127.0.0.1:50001";
32pub const DEFAULT_ENDPOINT_URL: &str = "http://127.0.0.1:50000";
33pub const DEFAULT_ICE_SERVERS: &str = "stun://stun.l.google.com:19302";
34pub const DEFAULT_STABILIZE_INTERVAL: u64 = 3;
35pub const DEFAULT_STORAGE_CAPACITY: u32 = 200000000;
36
37pub fn get_storage_location<P>(prefix: P, path: P) -> String
38where P: AsRef<std::path::Path> {
39 let home_dir = env::var_os("HOME").map(PathBuf::from);
40 let storage_path = match home_dir {
41 Some(dir) => dir.join(prefix).join(path),
42 None => std::path::Path::new("data").join(prefix).join(path),
43 };
44 storage_path.to_string_lossy().to_string()
45}
46
47#[derive(Debug, Clone, Deserialize, Serialize)]
48pub struct Config {
49 pub network_id: u32,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub ecdsa_key: Option<SecretKey>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub session_manager: Option<String>,
54 pub session_sk: Option<String>,
55 pub internal_api_port: u16,
56 pub external_api_addr: String,
57 pub endpoint_url: String,
58 pub ice_servers: String,
59 pub stabilize_interval: u64,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub external_ip: Option<String>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub webrtc_udp_port_min: Option<u16>,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub webrtc_udp_port_max: Option<u16>,
66 pub data_storage: StorageConfig,
67 pub measure_storage: StorageConfig,
68}
69
70impl TryFrom<Config> for ProcessorConfigSerialized {
71 type Error = Error;
72 fn try_from(config: Config) -> Result<Self> {
73 let session_sk: String = if let Some(sk) = config.ecdsa_key {
75 tracing::warn!("Field `ecdsa_key` is deprecated, use `session_sk` instead.");
76 SessionSk::new_with_seckey(&sk)
77 .and_then(|session_sk| session_sk.dump())
78 .map_err(|e| Error::VerifyError(e.to_string()))?
79 } else if let Some(ssk) = config.session_manager {
80 tracing::warn!("Field `session_manager` is deprecated, use `session_sk` instead.");
81 ssk
82 } else {
83 let Some(ssk_file) = config.session_sk else {
84 return Err(Error::InvalidData);
85 };
86 let ssk_file_expand_home = expand_home(&ssk_file)?;
87 fs::read_to_string(ssk_file_expand_home).unwrap_or_else(|e| {
88 tracing::warn!("Read session_sk file failed: {e:?}. Handling it as raw session_sk string. This mode is deprecated. please use a file path.");
89 ssk_file
90 })
91 };
92
93 let mut cs = Self::new(
94 config.network_id,
95 config.ice_servers,
96 session_sk,
97 config.stabilize_interval,
98 );
99
100 cs = if let Some(ext_ip) = config.external_ip {
101 cs.external_address(ext_ip)
102 } else {
103 cs
104 };
105 let udp_range = crate::processor::parse_webrtc_udp_port_range(
106 config.webrtc_udp_port_min,
107 config.webrtc_udp_port_max,
108 )?;
109 cs = if let Some(range) = udp_range {
110 cs.webrtc_udp_port_range(range)
111 } else {
112 cs
113 };
114
115 Ok(cs)
116 }
117}
118
119impl TryFrom<Config> for ProcessorConfig {
120 type Error = Error;
121 fn try_from(config: Config) -> Result<Self> {
122 ProcessorConfigSerialized::try_from(config).and_then(Self::try_from)
123 }
124}
125
126impl Config {
127 pub fn new<P>(session_sk: P) -> Self
128 where P: AsRef<std::path::Path> {
129 let session_sk = session_sk.as_ref().to_string_lossy().to_string();
130 Self {
131 network_id: DEFAULT_NETWORK_ID,
132 ecdsa_key: None,
133 session_manager: None,
134 session_sk: Some(session_sk),
135 internal_api_port: DEFAULT_INTERNAL_API_PORT,
136 external_api_addr: DEFAULT_EXTERNAL_API_ADDR.to_string(),
137 endpoint_url: DEFAULT_ENDPOINT_URL.to_string(),
138 ice_servers: DEFAULT_ICE_SERVERS.to_string(),
139 stabilize_interval: DEFAULT_STABILIZE_INTERVAL,
140 external_ip: None,
141 webrtc_udp_port_min: None,
142 webrtc_udp_port_max: None,
143 data_storage: DEFAULT_DATA_STORAGE_CONFIG.clone(),
144 measure_storage: DEFAULT_MEASURE_STORAGE_CONFIG.clone(),
145 }
146 }
147
148 pub fn write_fs<P>(&self, path: P) -> Result<String>
149 where P: AsRef<std::path::Path> {
150 let path = expand_home(path)?;
151 ensure_parent_dir(&path)?;
152 let f =
153 fs::File::create(path.as_path()).map_err(|e| Error::CreateFileError(e.to_string()))?;
154 let f_writer = io::BufWriter::new(f);
155 serde_yaml::to_writer(f_writer, self).map_err(|_| Error::EncodeError)?;
156 path.to_str()
157 .map(str::to_owned)
158 .ok_or_else(|| Error::PathUtf8Error(path.display().to_string()))
159 }
160
161 pub fn read_fs<P>(path: P) -> Result<Config>
162 where P: AsRef<std::path::Path> {
163 let path = expand_home(path)?;
164 tracing::debug!("Read config from: {:?}", path);
165 let f = fs::File::open(path).map_err(|e| Error::OpenFileError(e.to_string()))?;
166 let f_rdr = io::BufReader::new(f);
167 serde_yaml::from_reader(f_rdr).map_err(|_| Error::EncodeError)
168 }
169}
170
171#[derive(Debug, Clone, Deserialize, Serialize)]
172pub struct StorageConfig {
173 pub path: String,
174 pub capacity: u32,
175}
176
177impl StorageConfig {
178 pub fn new(path: &str, capacity: u32) -> Self {
179 Self {
180 path: path.to_string(),
181 capacity,
182 }
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 fn dumped_session_sk() -> String {
191 let key = SecretKey::random();
192 let session = match SessionSk::new_with_seckey(&key) {
193 Ok(session) => session,
194 Err(error) => panic!("session key construction failed: {error}"),
195 };
196 match session.dump() {
197 Ok(dump) => dump,
198 Err(error) => panic!("session key dump failed: {error}"),
199 }
200 }
201
202 #[test]
203 fn test_deserialization_with_missed_field() {
204 let yaml = r#"
205network_id: 1
206session_sk: session_sk
207internal_api_port: 50000
208external_api_addr: 127.0.0.1:50001
209endpoint_url: http://127.0.0.1:50000
210ice_servers: stun://stun.l.google.com:19302
211stabilize_interval: 3
212external_ip: null
213webrtc_udp_port_min: null
214webrtc_udp_port_max: null
215data_storage:
216 path: /Users/foo/.rings/data
217 capacity: 200000000
218measure_storage:
219 path: /Users/foo/.rings/measure
220 capacity: 200000000
221"#;
222 let cfg: Config = serde_yaml::from_str(yaml).unwrap();
223 assert_eq!(cfg.network_id, 1);
224 }
225
226 #[test]
227 fn config_with_valid_webrtc_udp_range_builds_processor_config() {
228 let mut config = Config::new(dumped_session_sk());
229 config.webrtc_udp_port_min = Some(49160);
230 config.webrtc_udp_port_max = Some(49200);
231
232 let processor_config = ProcessorConfig::try_from(config);
233
234 assert!(matches!(
235 processor_config.and_then(|config| config.webrtc_udp_port_range()),
236 Ok(Some(range)) if range.min() == 49160 && range.max() == 49200
237 ));
238 }
239
240 #[test]
241 fn config_with_partial_webrtc_udp_range_is_rejected() {
242 let mut config = Config::new(dumped_session_sk());
243 config.webrtc_udp_port_min = Some(49160);
244
245 let processor_config = ProcessorConfig::try_from(config);
246
247 assert!(matches!(
248 processor_config,
249 Err(Error::IncompleteWebrtcUdpPortRange {
250 min: Some(49160),
251 max: None
252 })
253 ));
254 }
255}