1use serde::{Deserialize, Serialize};
4use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
5use std::path::PathBuf;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
9#[serde(rename_all = "lowercase")]
10pub enum IpVersion {
11 Ipv4,
13 Ipv6,
15 #[default]
17 Dual,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
22#[serde(rename_all = "lowercase")]
23pub enum UpgradeChannel {
24 #[default]
26 Stable,
27 Beta,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
33#[serde(rename_all = "lowercase")]
34pub enum NetworkMode {
35 #[default]
37 Production,
38 Testnet,
41 Development,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct TestnetConfig {
49 #[serde(default = "default_testnet_max_per_asn")]
52 pub max_nodes_per_asn: usize,
53
54 #[serde(default = "default_testnet_max_per_64")]
57 pub max_nodes_per_64: usize,
58
59 #[serde(default)]
62 pub enforce_age_requirements: bool,
63
64 #[serde(default)]
67 pub enable_geo_checks: bool,
68}
69
70impl Default for TestnetConfig {
71 fn default() -> Self {
72 Self {
73 max_nodes_per_asn: default_testnet_max_per_asn(),
74 max_nodes_per_64: default_testnet_max_per_64(),
75 enforce_age_requirements: false,
76 enable_geo_checks: false,
77 }
78 }
79}
80
81const fn default_testnet_max_per_asn() -> usize {
82 5000
83}
84
85const fn default_testnet_max_per_64() -> usize {
86 100
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct NodeConfig {
92 #[serde(default = "default_root_dir")]
94 pub root_dir: PathBuf,
95
96 #[serde(default)]
98 pub port: u16,
99
100 #[serde(default)]
102 pub ip_version: IpVersion,
103
104 #[serde(default)]
106 pub bootstrap: Vec<SocketAddr>,
107
108 #[serde(default)]
110 pub network_mode: NetworkMode,
111
112 #[serde(default)]
115 pub testnet: TestnetConfig,
116
117 #[serde(default)]
119 pub upgrade: UpgradeConfig,
120
121 #[serde(default)]
123 pub payment: PaymentConfig,
124
125 #[serde(default)]
127 pub attestation: AttestationNodeConfig,
128
129 #[serde(default)]
131 pub bootstrap_cache: BootstrapCacheConfig,
132
133 #[serde(default)]
135 pub storage: StorageConfig,
136
137 #[serde(default = "default_log_level")]
139 pub log_level: String,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct UpgradeConfig {
145 #[serde(default)]
147 pub enabled: bool,
148
149 #[serde(default)]
151 pub channel: UpgradeChannel,
152
153 #[serde(default = "default_check_interval")]
155 pub check_interval_hours: u64,
156
157 #[serde(default = "default_github_repo")]
159 pub github_repo: String,
160
161 #[serde(default = "default_staged_rollout_hours")]
169 pub staged_rollout_hours: u64,
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
174#[serde(rename_all = "kebab-case")]
175pub enum EvmNetworkConfig {
176 #[default]
178 ArbitrumOne,
179 ArbitrumSepolia,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct PaymentConfig {
189 #[serde(default = "default_payment_enabled")]
191 pub enabled: bool,
192
193 #[serde(default = "default_cache_capacity")]
195 pub cache_capacity: usize,
196
197 #[serde(default)]
200 pub rewards_address: Option<String>,
201
202 #[serde(default)]
204 pub evm_network: EvmNetworkConfig,
205
206 #[serde(default = "default_metrics_port")]
209 pub metrics_port: u16,
210}
211
212impl Default for PaymentConfig {
213 fn default() -> Self {
214 Self {
215 enabled: default_payment_enabled(),
216 cache_capacity: default_cache_capacity(),
217 rewards_address: None,
218 evm_network: EvmNetworkConfig::default(),
219 metrics_port: default_metrics_port(),
220 }
221 }
222}
223
224const fn default_metrics_port() -> u16 {
225 9100
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
236#[serde(rename_all = "lowercase")]
237pub enum AttestationMode {
238 #[default]
241 Off,
242 Soft,
245 Hard,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
273pub struct AttestationNodeConfig {
274 #[serde(default)]
277 pub enabled: bool,
278
279 #[serde(default)]
282 pub mode: AttestationMode,
283
284 #[serde(default = "default_require_pq_secure")]
289 pub require_pq_secure: bool,
290
291 #[serde(default)]
295 pub allowed_binary_hashes: Vec<String>,
296
297 #[serde(default = "default_sunset_grace_days")]
301 pub sunset_grace_days: u32,
302}
303
304impl Default for AttestationNodeConfig {
305 fn default() -> Self {
306 Self {
307 enabled: false,
308 mode: AttestationMode::Off,
309 require_pq_secure: default_require_pq_secure(),
310 allowed_binary_hashes: Vec::new(),
311 sunset_grace_days: default_sunset_grace_days(),
312 }
313 }
314}
315
316impl AttestationNodeConfig {
317 #[must_use]
319 pub fn development() -> Self {
320 Self {
321 enabled: true,
322 mode: AttestationMode::Soft,
323 require_pq_secure: false,
324 allowed_binary_hashes: Vec::new(),
325 sunset_grace_days: 365,
326 }
327 }
328
329 #[must_use]
331 pub fn production(allowed_binary_hashes: Vec<String>) -> Self {
332 Self {
333 enabled: true,
334 mode: AttestationMode::Hard,
335 require_pq_secure: true,
336 allowed_binary_hashes,
337 sunset_grace_days: 30,
338 }
339 }
340}
341
342const fn default_require_pq_secure() -> bool {
343 true
344}
345
346const fn default_sunset_grace_days() -> u32 {
347 30
348}
349
350const fn default_payment_enabled() -> bool {
351 true
352}
353
354const fn default_cache_capacity() -> usize {
355 100_000
356}
357
358impl Default for NodeConfig {
359 fn default() -> Self {
360 Self {
361 root_dir: default_root_dir(),
362 port: 0,
363 ip_version: IpVersion::default(),
364 bootstrap: Vec::new(),
365 network_mode: NetworkMode::default(),
366 testnet: TestnetConfig::default(),
367 upgrade: UpgradeConfig::default(),
368 payment: PaymentConfig::default(),
369 attestation: AttestationNodeConfig::default(),
370 bootstrap_cache: BootstrapCacheConfig::default(),
371 storage: StorageConfig::default(),
372 log_level: default_log_level(),
373 }
374 }
375}
376
377impl NodeConfig {
378 #[must_use]
384 pub fn testnet() -> Self {
385 Self {
386 network_mode: NetworkMode::Testnet,
387 testnet: TestnetConfig::default(),
388 bootstrap: default_testnet_bootstrap(),
389 ..Self::default()
390 }
391 }
392
393 #[must_use]
397 pub fn development() -> Self {
398 Self {
399 network_mode: NetworkMode::Development,
400 testnet: TestnetConfig {
401 max_nodes_per_asn: usize::MAX,
402 max_nodes_per_64: usize::MAX,
403 enforce_age_requirements: false,
404 enable_geo_checks: false,
405 },
406 ..Self::default()
407 }
408 }
409
410 #[must_use]
412 pub fn is_relaxed(&self) -> bool {
413 !matches!(self.network_mode, NetworkMode::Production)
414 }
415
416 pub fn from_file(path: &std::path::Path) -> crate::Result<Self> {
422 let content = std::fs::read_to_string(path)?;
423 toml::from_str(&content).map_err(|e| crate::Error::Config(e.to_string()))
424 }
425
426 pub fn to_file(&self, path: &std::path::Path) -> crate::Result<()> {
432 let content =
433 toml::to_string_pretty(self).map_err(|e| crate::Error::Config(e.to_string()))?;
434 std::fs::write(path, content)?;
435 Ok(())
436 }
437}
438
439impl Default for UpgradeConfig {
440 fn default() -> Self {
441 Self {
442 enabled: false,
443 channel: UpgradeChannel::default(),
444 check_interval_hours: default_check_interval(),
445 github_repo: default_github_repo(),
446 staged_rollout_hours: default_staged_rollout_hours(),
447 }
448 }
449}
450
451fn default_github_repo() -> String {
452 "dirvine/saorsa-node".to_string()
453}
454
455fn default_root_dir() -> PathBuf {
456 directories::ProjectDirs::from("", "", "saorsa").map_or_else(
457 || PathBuf::from(".saorsa"),
458 |dirs| dirs.data_dir().to_path_buf(),
459 )
460}
461
462fn default_log_level() -> String {
463 "info".to_string()
464}
465
466const fn default_check_interval() -> u64 {
467 1 }
469
470const fn default_staged_rollout_hours() -> u64 {
471 1 }
473
474#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct BootstrapCacheConfig {
486 #[serde(default = "default_bootstrap_cache_enabled")]
489 pub enabled: bool,
490
491 #[serde(default)]
494 pub cache_dir: Option<PathBuf>,
495
496 #[serde(default = "default_bootstrap_max_contacts")]
499 pub max_contacts: usize,
500
501 #[serde(default = "default_bootstrap_stale_days")]
505 pub stale_threshold_days: u64,
506}
507
508impl Default for BootstrapCacheConfig {
509 fn default() -> Self {
510 Self {
511 enabled: default_bootstrap_cache_enabled(),
512 cache_dir: None,
513 max_contacts: default_bootstrap_max_contacts(),
514 stale_threshold_days: default_bootstrap_stale_days(),
515 }
516 }
517}
518
519const fn default_bootstrap_cache_enabled() -> bool {
520 true
521}
522
523const fn default_bootstrap_max_contacts() -> usize {
524 10_000
525}
526
527const fn default_bootstrap_stale_days() -> u64 {
528 7
529}
530
531#[derive(Debug, Clone, Serialize, Deserialize)]
542pub struct StorageConfig {
543 #[serde(default = "default_storage_enabled")]
546 pub enabled: bool,
547
548 #[serde(default)]
551 pub max_chunks: usize,
552
553 #[serde(default = "default_storage_verify_on_read")]
556 pub verify_on_read: bool,
557}
558
559impl Default for StorageConfig {
560 fn default() -> Self {
561 Self {
562 enabled: default_storage_enabled(),
563 max_chunks: 0,
564 verify_on_read: default_storage_verify_on_read(),
565 }
566 }
567}
568
569const fn default_storage_enabled() -> bool {
570 true
571}
572
573const fn default_storage_verify_on_read() -> bool {
574 true
575}
576
577fn default_testnet_bootstrap() -> Vec<SocketAddr> {
583 vec![
584 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(165, 22, 4, 178), 12000)),
586 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(164, 92, 111, 156), 12000)),
588 ]
589}