1#![deny(clippy::pedantic)]
2#![allow(clippy::cast_possible_truncation)]
3#![allow(clippy::cast_possible_wrap)]
4#![allow(clippy::cast_precision_loss)]
5#![allow(clippy::cast_sign_loss)]
6#![allow(clippy::doc_markdown)]
7#![allow(clippy::missing_errors_doc)]
8#![allow(clippy::missing_panics_doc)]
9#![allow(clippy::module_name_repetitions)]
10#![allow(clippy::must_use_candidate)]
11#![allow(clippy::return_self_not_must_use)]
12#![allow(clippy::similar_names)]
13#![allow(clippy::too_many_lines)]
14
15extern crate fedimint_core;
16
17use std::fs;
18use std::path::{Path, PathBuf};
19
20use config::io::{read_server_config, PLAINTEXT_PASSWORD};
21use config::ServerConfig;
22use fedimint_aead::random_salt;
23use fedimint_core::config::ServerModuleInitRegistry;
24use fedimint_core::db::Database;
25use fedimint_core::epoch::ConsensusItem;
26use fedimint_core::task::TaskGroup;
27use fedimint_core::util::write_new;
28use fedimint_logging::{LOG_CONSENSUS, LOG_CORE};
29use net::api::ApiSecrets;
30use tracing::{info, warn};
31
32use crate::config::api::{ConfigGenApi, ConfigGenSettings};
33use crate::config::io::{write_server_config, SALT_FILE};
34use crate::metrics::initialize_gauge_metrics;
35use crate::net::api::announcement::start_api_announcement_service;
36use crate::net::api::RpcHandlerCtx;
37use crate::net::connect::TlsTcpConnector;
38
39pub mod envs;
40pub mod metrics;
41
42pub mod consensus;
44
45pub mod net;
47
48pub mod config;
50
51pub mod multiplexed;
53
54pub async fn run(
55 data_dir: PathBuf,
56 force_api_secrets: ApiSecrets,
57 settings: ConfigGenSettings,
58 db: Database,
59 code_version_str: String,
60 module_init_registry: &ServerModuleInitRegistry,
61 task_group: TaskGroup,
62) -> anyhow::Result<()> {
63 let cfg = match get_config(&data_dir)? {
64 Some(cfg) => cfg,
65 None => {
66 run_config_gen(
67 data_dir.clone(),
68 settings.clone(),
69 db.clone(),
70 code_version_str.clone(),
71 task_group.make_subgroup(),
72 force_api_secrets.clone(),
73 )
74 .await?
75 }
76 };
77
78 let decoders = module_init_registry.decoders_strict(
79 cfg.consensus
80 .modules
81 .iter()
82 .map(|(id, config)| (*id, &config.kind)),
83 )?;
84
85 let db = db.with_decoders(decoders);
86
87 initialize_gauge_metrics(&db).await;
88
89 start_api_announcement_service(&db, &task_group, &cfg, force_api_secrets.get_active()).await;
90
91 consensus::run(
92 settings.p2p_bind,
93 settings.api_bind,
94 cfg,
95 db,
96 module_init_registry.clone(),
97 &task_group,
98 force_api_secrets,
99 data_dir,
100 code_version_str,
101 )
102 .await?;
103
104 info!(target: LOG_CONSENSUS, "Shutting down tasks");
105
106 task_group.shutdown();
107
108 Ok(())
109}
110
111pub fn get_config(data_dir: &Path) -> anyhow::Result<Option<ServerConfig>> {
112 let path = data_dir.join(PLAINTEXT_PASSWORD);
114 if let Ok(password_untrimmed) = fs::read_to_string(&path) {
115 let password = password_untrimmed.trim_matches('\n');
119 let password_fully_trimmed = password.trim();
121 if password_fully_trimmed != password {
122 warn!(
123 target: LOG_CORE,
124 path = %path.display(),
125 "Password in the password file contains leading/trailing whitespaces. This will an error in the future."
126 );
127 }
128 return Ok(Some(read_server_config(password, data_dir)?));
129 }
130
131 Ok(None)
132}
133
134pub async fn run_config_gen(
135 data_dir: PathBuf,
136 settings: ConfigGenSettings,
137 db: Database,
138 code_version_str: String,
139 task_group: TaskGroup,
140 force_api_secrets: ApiSecrets,
141) -> anyhow::Result<ServerConfig> {
142 info!(target: LOG_CONSENSUS, "Starting config gen");
143
144 initialize_gauge_metrics(&db).await;
145
146 let (cfg_sender, mut cfg_receiver) = tokio::sync::mpsc::channel(1);
147
148 let config_gen = ConfigGenApi::new(
149 settings.p2p_bind,
150 settings.clone(),
151 db.clone(),
152 cfg_sender,
153 &task_group,
154 code_version_str.clone(),
155 force_api_secrets.get_active(),
156 );
157
158 let mut rpc_module = RpcHandlerCtx::new_module(config_gen);
159
160 net::api::attach_endpoints(&mut rpc_module, config::api::server_endpoints(), None);
161
162 let api_handler = net::api::spawn(
163 "config-gen",
164 settings.api_bind,
165 rpc_module,
166 10,
167 force_api_secrets.clone(),
168 )
169 .await;
170
171 let cfg = cfg_receiver.recv().await.expect("should not close");
172
173 api_handler
174 .stop()
175 .expect("Config api should still be running");
176
177 api_handler.stopped().await;
178
179 write_new(data_dir.join(PLAINTEXT_PASSWORD), &cfg.private.api_auth.0)?;
181 write_new(data_dir.join(SALT_FILE), random_salt())?;
182 write_server_config(
183 &cfg,
184 &data_dir,
185 &cfg.private.api_auth.0,
186 &settings.registry,
187 force_api_secrets.get_active(),
188 )?;
189
190 Ok(cfg)
191}