1use std::{
4 collections::{BTreeMap, BTreeSet},
5 fmt::{self, Display},
6 path::PathBuf,
7};
8
9use ave_common::identity::{HashAlgorithm, KeyPairAlgorithm};
10use ave_network::Config as NetworkConfig;
11use serde::{Deserialize, Deserializer, Serialize};
12
13use crate::{helpers::sink::TokenResponse, subject::sinkdata::SinkTypes};
14
15#[derive(Clone, Debug, Deserialize, Serialize)]
17#[serde(default)]
18#[serde(rename_all = "snake_case")]
19pub struct Config {
20 pub keypair_algorithm: KeyPairAlgorithm,
22 pub hash_algorithm: HashAlgorithm,
24 pub internal_db: AveInternalDBConfig,
26 pub external_db: AveExternalDBConfig,
28 pub network: NetworkConfig,
30 pub contracts_path: PathBuf,
32 pub always_accept: bool,
34 pub safe_mode: bool,
36 pub tracking_size: usize,
38 pub is_service: bool,
40 pub only_clear_events: bool,
42 pub sync: SyncConfig,
44 pub spec: Option<MachineSpec>,
47}
48
49impl Default for Config {
50 fn default() -> Self {
51 Self {
52 keypair_algorithm: KeyPairAlgorithm::Ed25519,
53 hash_algorithm: HashAlgorithm::Blake3,
54 internal_db: Default::default(),
55 external_db: Default::default(),
56 network: Default::default(),
57 contracts_path: PathBuf::from("contracts"),
58 always_accept: Default::default(),
59 safe_mode: false,
60 tracking_size: 100,
61 is_service: false,
62 only_clear_events: false,
63 sync: Default::default(),
64 spec: None,
65 }
66 }
67}
68
69#[derive(Clone, Debug, Deserialize, Serialize)]
70#[serde(default)]
71#[serde(rename_all = "snake_case")]
72pub struct SyncConfig {
73 pub ledger_batch_size: usize,
74 pub governance: GovernanceSyncConfig,
75 pub tracker: TrackerSyncConfig,
76 pub update: UpdateSyncConfig,
77 pub reboot: RebootSyncConfig,
78}
79
80impl Default for SyncConfig {
81 fn default() -> Self {
82 Self {
83 ledger_batch_size: 100,
84 governance: GovernanceSyncConfig::default(),
85 tracker: TrackerSyncConfig::default(),
86 update: UpdateSyncConfig::default(),
87 reboot: RebootSyncConfig::default(),
88 }
89 }
90}
91
92#[derive(Clone, Debug, Deserialize, Serialize)]
93#[serde(default)]
94#[serde(rename_all = "snake_case")]
95pub struct UpdateSyncConfig {
96 pub round_retry_interval_secs: u64,
98 pub max_round_retries: usize,
100 pub witness_retry_count: usize,
102 pub witness_retry_interval_secs: u64,
104}
105
106impl Default for UpdateSyncConfig {
107 fn default() -> Self {
108 Self {
109 round_retry_interval_secs: 8,
110 max_round_retries: 3,
111 witness_retry_count: 1,
112 witness_retry_interval_secs: 5,
113 }
114 }
115}
116
117#[derive(Clone, Debug, Deserialize, Serialize)]
118#[serde(default)]
119#[serde(rename_all = "snake_case")]
120pub struct RebootSyncConfig {
121 pub stability_check_interval_secs: u64,
123 pub stability_check_max_retries: u64,
125 pub diff_retry_schedule_secs: Vec<u64>,
127 pub timeout_retry_schedule_secs: Vec<u64>,
129}
130
131impl Default for RebootSyncConfig {
132 fn default() -> Self {
133 Self {
134 stability_check_interval_secs: 5,
135 stability_check_max_retries: 3,
136 diff_retry_schedule_secs: vec![10, 20, 30, 60],
137 timeout_retry_schedule_secs:
138 default_reboot_timeout_retry_schedule_secs(),
139 }
140 }
141}
142
143#[cfg(any(test, feature = "test"))]
144fn default_reboot_timeout_retry_schedule_secs() -> Vec<u64> {
145 vec![5, 5, 5, 5]
146}
147
148#[cfg(not(any(test, feature = "test")))]
149fn default_reboot_timeout_retry_schedule_secs() -> Vec<u64> {
150 vec![30, 60, 120, 300]
151}
152
153#[derive(Clone, Debug, Deserialize, Serialize)]
154#[serde(default)]
155#[serde(rename_all = "snake_case")]
156pub struct GovernanceSyncConfig {
157 pub interval_secs: u64,
159 pub sample_size: usize,
161 pub response_timeout_secs: u64,
163}
164
165impl Default for GovernanceSyncConfig {
166 fn default() -> Self {
167 Self {
168 interval_secs: 60,
169 sample_size: 3,
170 response_timeout_secs: 10,
171 }
172 }
173}
174
175#[derive(Clone, Debug, Deserialize, Serialize)]
176#[serde(default)]
177#[serde(rename_all = "snake_case")]
178pub struct TrackerSyncConfig {
179 pub interval_secs: u64,
181 pub page_size: usize,
183 pub response_timeout_secs: u64,
185 pub update_batch_size: usize,
187 pub update_timeout_secs: u64,
189}
190
191impl Default for TrackerSyncConfig {
192 fn default() -> Self {
193 Self {
194 interval_secs: 30,
195 page_size: 50,
196 response_timeout_secs: 10,
197 update_batch_size: 2,
198 update_timeout_secs: 10,
199 }
200 }
201}
202
203#[derive(Serialize, Deserialize, Debug, Clone)]
211#[serde(rename_all = "snake_case")]
212pub enum MachineSpec {
213 Profile(MachineProfile),
215 Custom {
217 ram_mb: u64,
219 cpu_cores: usize,
221 },
222}
223
224#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
238#[serde(rename_all = "snake_case")]
239pub enum MachineProfile {
240 Nano,
242 Micro,
244 Small,
246 Medium,
248 Large,
250 XLarge,
252 #[serde(rename = "2xlarge")]
254 XXLarge,
255}
256
257impl MachineProfile {
258 pub const fn ram_mb(self) -> u64 {
260 match self {
261 Self::Nano => 512,
262 Self::Micro => 1_024,
263 Self::Small => 2_048,
264 Self::Medium => 4_096,
265 Self::Large => 8_192,
266 Self::XLarge => 16_384,
267 Self::XXLarge => 32_768,
268 }
269 }
270
271 pub const fn cpu_cores(self) -> usize {
273 match self {
274 Self::Nano => 2,
275 Self::Micro => 2,
276 Self::Small => 2,
277 Self::Medium => 2,
278 Self::Large => 2,
279 Self::XLarge => 4,
280 Self::XXLarge => 8,
281 }
282 }
283}
284
285impl Display for MachineProfile {
286 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287 match self {
288 Self::Nano => write!(f, "nano"),
289 Self::Micro => write!(f, "micro"),
290 Self::Small => write!(f, "small"),
291 Self::Medium => write!(f, "medium"),
292 Self::Large => write!(f, "large"),
293 Self::XLarge => write!(f, "xlarge"),
294 Self::XXLarge => write!(f, "2xlarge"),
295 }
296 }
297}
298
299pub struct ResolvedSpec {
303 pub ram_mb: u64,
305 pub cpu_cores: usize,
307}
308
309pub fn resolve_spec(spec: Option<&MachineSpec>) -> ResolvedSpec {
315 match spec {
316 Some(MachineSpec::Profile(p)) => ResolvedSpec {
317 ram_mb: p.ram_mb(),
318 cpu_cores: p.cpu_cores(),
319 },
320 Some(MachineSpec::Custom { ram_mb, cpu_cores }) => ResolvedSpec {
321 ram_mb: *ram_mb,
322 cpu_cores: *cpu_cores,
323 },
324 None => ResolvedSpec {
325 ram_mb: detect_ram_mb(),
326 cpu_cores: detect_cpu_cores(),
327 },
328 }
329}
330
331pub(crate) fn detect_ram_mb() -> u64 {
332 #[cfg(target_os = "linux")]
333 {
334 if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
335 for line in meminfo.lines() {
336 if let Some(rest) = line.strip_prefix("MemTotal:")
337 && let Some(kb_str) = rest.split_whitespace().next()
338 && let Ok(kb) = kb_str.parse::<u64>()
339 {
340 return kb / 1024;
341 }
342 }
343 }
344 }
345 4_096
346}
347
348pub(crate) fn detect_cpu_cores() -> usize {
349 std::thread::available_parallelism()
350 .map(|n| n.get())
351 .unwrap_or(2)
352}
353
354impl From<MachineProfile> for ave_network::MachineProfile {
357 fn from(p: MachineProfile) -> Self {
358 match p {
359 MachineProfile::Nano => Self::Nano,
360 MachineProfile::Micro => Self::Micro,
361 MachineProfile::Small => Self::Small,
362 MachineProfile::Medium => Self::Medium,
363 MachineProfile::Large => Self::Large,
364 MachineProfile::XLarge => Self::XLarge,
365 MachineProfile::XXLarge => Self::XXLarge,
366 }
367 }
368}
369
370impl From<MachineSpec> for ave_network::MachineSpec {
371 fn from(spec: MachineSpec) -> Self {
372 match spec {
373 MachineSpec::Profile(p) => Self::Profile(p.into()),
374 MachineSpec::Custom { ram_mb, cpu_cores } => {
375 Self::Custom { ram_mb, cpu_cores }
376 }
377 }
378 }
379}
380
381impl From<MachineProfile> for ave_actors::MachineProfile {
382 fn from(p: MachineProfile) -> Self {
383 match p {
384 MachineProfile::Nano => Self::Nano,
385 MachineProfile::Micro => Self::Micro,
386 MachineProfile::Small => Self::Small,
387 MachineProfile::Medium => Self::Medium,
388 MachineProfile::Large => Self::Large,
389 MachineProfile::XLarge => Self::XLarge,
390 MachineProfile::XXLarge => Self::XXLarge,
391 }
392 }
393}
394
395impl From<MachineSpec> for ave_actors::MachineSpec {
396 fn from(spec: MachineSpec) -> Self {
397 match spec {
398 MachineSpec::Profile(p) => Self::Profile(p.into()),
399 MachineSpec::Custom { ram_mb, cpu_cores } => {
400 Self::Custom { ram_mb, cpu_cores }
401 }
402 }
403 }
404}
405
406#[derive(Debug, Clone, Deserialize, Serialize, Default)]
407#[serde(default)]
408pub struct AveInternalDBConfig {
409 #[serde(deserialize_with = "AveInternalDBFeatureConfig::deserialize_db")]
410 pub db: AveInternalDBFeatureConfig,
411 pub durability: bool,
412}
413
414#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
416pub enum AveInternalDBFeatureConfig {
417 #[cfg(feature = "rocksdb")]
419 Rocksdb {
420 path: PathBuf,
422 },
423 #[cfg(feature = "sqlite")]
425 Sqlite {
426 path: PathBuf,
428 },
429}
430
431impl Default for AveInternalDBFeatureConfig {
432 fn default() -> Self {
433 #[cfg(feature = "rocksdb")]
434 return AveInternalDBFeatureConfig::Rocksdb {
435 path: PathBuf::from("db").join("local").join("rocksdb"),
436 };
437 #[cfg(feature = "sqlite")]
438 return Self::Sqlite {
439 path: PathBuf::from("db").join("local").join("sqlite"),
440 };
441 }
442}
443
444impl AveInternalDBFeatureConfig {
445 pub fn build(path: &PathBuf) -> Self {
446 #[cfg(feature = "rocksdb")]
447 return AveInternalDBFeatureConfig::Rocksdb {
448 path: path.to_owned(),
449 };
450 #[cfg(feature = "sqlite")]
451 return Self::Sqlite {
452 path: path.to_owned(),
453 };
454 }
455
456 pub fn deserialize_db<'de, D>(deserializer: D) -> Result<Self, D::Error>
457 where
458 D: Deserializer<'de>,
459 {
460 let path: String = String::deserialize(deserializer)?;
461 #[cfg(feature = "rocksdb")]
462 return Ok(AveInternalDBFeatureConfig::Rocksdb {
463 path: PathBuf::from(path),
464 });
465 #[cfg(feature = "sqlite")]
466 return Ok(Self::Sqlite {
467 path: PathBuf::from(path),
468 });
469 }
470}
471
472impl fmt::Display for AveInternalDBFeatureConfig {
473 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474 match self {
475 #[cfg(feature = "rocksdb")]
476 AveInternalDBFeatureConfig::Rocksdb { .. } => write!(f, "Rocksdb"),
477 #[cfg(feature = "sqlite")]
478 Self::Sqlite { .. } => write!(f, "Sqlite"),
479 }
480 }
481}
482
483#[derive(Debug, Clone, Deserialize, Serialize, Default)]
484#[serde(default)]
485pub struct AveExternalDBConfig {
486 #[serde(deserialize_with = "AveExternalDBFeatureConfig::deserialize_db")]
487 pub db: AveExternalDBFeatureConfig,
488 pub durability: bool,
489}
490
491#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
493pub enum AveExternalDBFeatureConfig {
494 #[cfg(feature = "ext-sqlite")]
496 Sqlite {
497 path: PathBuf,
499 },
500}
501
502impl Default for AveExternalDBFeatureConfig {
503 fn default() -> Self {
504 #[cfg(feature = "ext-sqlite")]
505 return Self::Sqlite {
506 path: PathBuf::from("db").join("ext").join("sqlite"),
507 };
508 }
509}
510
511impl AveExternalDBFeatureConfig {
512 pub fn build(path: &PathBuf) -> Self {
513 #[cfg(feature = "ext-sqlite")]
514 return Self::Sqlite {
515 path: path.to_owned(),
516 };
517 }
518
519 pub fn deserialize_db<'de, D>(deserializer: D) -> Result<Self, D::Error>
520 where
521 D: Deserializer<'de>,
522 {
523 let path: String = String::deserialize(deserializer)?;
524 #[cfg(feature = "ext-sqlite")]
525 return Ok(Self::Sqlite {
526 path: PathBuf::from(path),
527 });
528 }
529}
530
531impl fmt::Display for AveExternalDBFeatureConfig {
532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533 write!(f, "Sqlite")
534 }
535}
536
537#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
538pub struct LoggingOutput {
539 pub stdout: bool,
540 pub file: bool,
541 pub api: bool,
542}
543
544impl Default for LoggingOutput {
545 fn default() -> Self {
546 Self {
547 stdout: true,
548 file: Default::default(),
549 api: Default::default(),
550 }
551 }
552}
553
554#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Default, Serialize)]
555#[serde(rename_all = "lowercase")]
556pub enum LoggingRotation {
557 #[default]
558 Size,
559 Hourly,
560 Daily,
561 Weekly,
562 Monthly,
563 Yearly,
564 Never,
565}
566
567impl Display for LoggingRotation {
568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569 match self {
570 Self::Size => write!(f, "size"),
571 Self::Hourly => write!(f, "hourly"),
572 Self::Daily => write!(f, "daily"),
573 Self::Weekly => write!(f, "weekly"),
574 Self::Monthly => write!(f, "monthly"),
575 Self::Yearly => write!(f, "yearly"),
576 Self::Never => write!(f, "never"),
577 }
578 }
579}
580
581#[derive(Clone, Debug, Deserialize, Serialize)]
582#[serde(default)]
583pub struct LoggingConfig {
584 pub output: LoggingOutput,
585 pub api_url: Option<String>,
586 pub file_path: PathBuf, pub rotation: LoggingRotation,
588 pub max_size: usize, pub max_files: usize, pub level: String,
594}
595
596impl Default for LoggingConfig {
597 fn default() -> Self {
598 Self {
599 output: LoggingOutput::default(),
600 api_url: None,
601 file_path: PathBuf::from("logs"),
602 rotation: LoggingRotation::default(),
603 max_size: 100 * 1024 * 1024,
604 max_files: 3,
605 level: "info".to_string(),
606 }
607 }
608}
609
610impl LoggingConfig {
611 pub const fn logs(&self) -> bool {
612 self.output.api || self.output.file || self.output.stdout
613 }
614}
615
616#[derive(Clone, Debug, Deserialize, Default, Eq, PartialEq, Serialize)]
617#[serde(default)]
618pub struct SinkServer {
619 pub server: String,
620 pub events: BTreeSet<SinkTypes>,
621 pub url: String,
622 pub auth: bool,
623 #[serde(default = "default_sink_concurrency")]
624 pub concurrency: usize,
625 #[serde(default = "default_sink_queue_capacity")]
626 pub queue_capacity: usize,
627 #[serde(default)]
628 pub queue_policy: SinkQueuePolicy,
629 #[serde(default)]
630 pub routing_strategy: SinkRoutingStrategy,
631 #[serde(default = "default_sink_connect_timeout_ms")]
632 pub connect_timeout_ms: u64,
633 #[serde(default = "default_sink_request_timeout_ms")]
634 pub request_timeout_ms: u64,
635 #[serde(default = "default_sink_max_retries")]
636 pub max_retries: usize,
637}
638
639#[derive(Default)]
640pub struct SinkAuth {
641 pub sink: SinkConfig,
642 pub token: Option<TokenResponse>,
643 pub password: String,
644 pub api_key: String,
645}
646
647#[derive(Clone, Debug, Deserialize, Default, Serialize)]
648#[serde(default)]
649pub struct SinkConfig {
650 pub sinks: BTreeMap<String, Vec<SinkServer>>,
651 pub auth: String,
652 pub username: String,
653}
654
655#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
656#[serde(rename_all = "snake_case")]
657pub enum SinkQueuePolicy {
658 DropOldest,
659 #[default]
660 DropNewest,
661}
662
663#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
664#[serde(rename_all = "snake_case")]
665pub enum SinkRoutingStrategy {
666 #[default]
667 OrderedBySubject,
668 UnorderedRoundRobin,
669}
670
671const fn default_sink_concurrency() -> usize {
672 2
673}
674
675const fn default_sink_queue_capacity() -> usize {
676 1024
677}
678
679const fn default_sink_connect_timeout_ms() -> u64 {
680 2_000
681}
682
683const fn default_sink_request_timeout_ms() -> u64 {
684 5_000
685}
686
687const fn default_sink_max_retries() -> usize {
688 2
689}