1use std::collections::BTreeMap;
2use std::net::SocketAddr;
3use std::sync::Arc;
4use std::time::Duration;
5
6use async_trait::async_trait;
7use bitcoin::hashes::sha256;
8use fedimint_api_client::api::{DynGlobalApi, StatusResponse};
9use fedimint_bitcoind::create_bitcoind;
10use fedimint_core::admin_client::{
11 ConfigGenConnectionsRequest, ConfigGenParamsConsensus, ConfigGenParamsRequest,
12 ConfigGenParamsResponse, PeerServerParams, ServerStatus,
13};
14use fedimint_core::config::{ConfigGenModuleParams, ServerModuleConfigGenParamsRegistry};
15use fedimint_core::core::ModuleInstanceId;
16use fedimint_core::db::Database;
17use fedimint_core::encoding::Encodable;
18use fedimint_core::endpoint_constants::{
19 ADD_CONFIG_GEN_PEER_ENDPOINT, AUTH_ENDPOINT, CHECK_BITCOIN_STATUS_ENDPOINT,
20 CONFIG_GEN_PEERS_ENDPOINT, CONSENSUS_CONFIG_GEN_PARAMS_ENDPOINT,
21 DEFAULT_CONFIG_GEN_PARAMS_ENDPOINT, RESTART_FEDERATION_SETUP_ENDPOINT, RUN_DKG_ENDPOINT,
22 SET_CONFIG_GEN_CONNECTIONS_ENDPOINT, SET_CONFIG_GEN_PARAMS_ENDPOINT, SET_PASSWORD_ENDPOINT,
23 START_CONSENSUS_ENDPOINT, STATUS_ENDPOINT, VERIFIED_CONFIGS_ENDPOINT,
24 VERIFY_CONFIG_HASH_ENDPOINT,
25};
26use fedimint_core::envs::BitcoinRpcConfig;
27use fedimint_core::module::{
28 api_endpoint, ApiAuth, ApiEndpoint, ApiEndpointContext, ApiError, ApiRequestErased, ApiVersion,
29};
30use fedimint_core::task::{sleep, TaskGroup};
31use fedimint_core::util::SafeUrl;
32use fedimint_core::PeerId;
33use fedimint_server_core::ServerModuleInitRegistry;
34use itertools::Itertools;
35use serde::{Deserialize, Serialize};
36use tokio::sync::mpsc::Sender;
37use tokio::sync::{Mutex, MutexGuard, RwLock};
38use tokio::time::Instant;
39use tokio_rustls::rustls;
40use tracing::{error, info};
41
42use crate::config::{gen_cert_and_key, ConfigGenParams, ServerConfig};
43use crate::envs::FM_PEER_ID_SORT_BY_URL_ENV;
44use crate::net::api::{check_auth, ApiResult, HasApiContext};
45
46#[derive(Clone)]
48pub struct ConfigGenApi {
49 state: Arc<Mutex<ConfigGenState>>,
51 db: Database,
53 config_generated_tx: Sender<ServerConfig>,
55 task_group: TaskGroup,
57 code_version_str: String,
59 api_secret: Option<String>,
61 p2p_bind_addr: SocketAddr,
62 bitcoin_status_cache: Arc<RwLock<Option<(Instant, BitcoinRpcConnectionStatus)>>>,
63 bitcoin_status_cache_duration: Duration,
64}
65
66impl ConfigGenApi {
67 pub fn new(
68 p2p_bind_addr: SocketAddr,
69 settings: ConfigGenSettings,
70 db: Database,
71 config_generated_tx: Sender<ServerConfig>,
72 task_group: &TaskGroup,
73 code_version_str: String,
74 api_secret: Option<String>,
75 ) -> Self {
76 let config_gen_api = Self {
77 state: Arc::new(Mutex::new(ConfigGenState::new(settings))),
78 db,
79 config_generated_tx,
80 task_group: task_group.clone(),
81 code_version_str,
82 api_secret,
83 p2p_bind_addr,
84 bitcoin_status_cache: Arc::new(RwLock::new(None)),
85 bitcoin_status_cache_duration: Duration::from_secs(60),
86 };
87 info!(target: fedimint_logging::LOG_NET_PEER_DKG, "Created new config gen Api");
88 config_gen_api
89 }
90
91 pub async fn set_password(&self, auth: ApiAuth) -> ApiResult<()> {
93 let mut state = self.require_status(ServerStatus::AwaitingPassword).await?;
94 let auth_trimmed = auth.0.trim();
95 if auth_trimmed != auth.0 {
96 return Err(ApiError::bad_request(
97 "Password contains leading/trailing whitespace".to_string(),
98 ));
99 }
100 state.auth = Some(auth);
101 state.status = ServerStatus::SharingConfigGenParams;
102 info!(
103 target: fedimint_logging::LOG_NET_PEER_DKG,
104 "Set password for config gen"
105 );
106 Ok(())
107 }
108
109 async fn require_status(&self, status: ServerStatus) -> ApiResult<MutexGuard<ConfigGenState>> {
110 let state = self.state.lock().await;
111 if state.status != status {
112 return Self::bad_request(&format!("Expected to be in {status:?} state"));
113 }
114 Ok(state)
115 }
116
117 async fn require_any_status(
118 &self,
119 statuses: &[ServerStatus],
120 ) -> ApiResult<MutexGuard<ConfigGenState>> {
121 let state = self.state.lock().await;
122 if !statuses.contains(&state.status) {
123 return Self::bad_request(&format!("Expected to be in one of {statuses:?} states"));
124 }
125 Ok(state)
126 }
127
128 pub async fn set_config_gen_connections(
130 &self,
131 request: ConfigGenConnectionsRequest,
132 ) -> ApiResult<()> {
133 {
134 let mut state = self
135 .require_status(ServerStatus::SharingConfigGenParams)
136 .await?;
137 state.set_request(request)?;
138 }
139 self.update_leader().await?;
140 Ok(())
141 }
142
143 async fn update_leader(&self) -> ApiResult<()> {
145 let state = self.state.lock().await.clone();
146 let local = state.local.clone();
147
148 if let Some(url) = local.and_then(|local| local.leader_api_url) {
149 DynGlobalApi::from_pre_peer_id_admin_endpoint(url, &self.api_secret)
150 .add_config_gen_peer(state.our_peer_info()?)
151 .await
152 .map_err(|_| ApiError::not_found("Unable to connect to the leader".to_string()))?;
153 }
154 Ok(())
155 }
156
157 pub async fn add_config_gen_peer(&self, peer: PeerServerParams) -> ApiResult<()> {
160 let mut state = self.state.lock().await;
161 state.peers.insert(peer.api_url.clone(), peer);
162 info!(target: fedimint_logging::LOG_NET_PEER_DKG, "New peer added to config gen");
163 Ok(())
164 }
165
166 pub async fn config_gen_peers(&self) -> ApiResult<Vec<PeerServerParams>> {
168 let state = self.state.lock().await;
169 Ok(state.get_peer_info().into_values().collect())
170 }
171
172 pub async fn default_config_gen_params(&self) -> ApiResult<ConfigGenParamsRequest> {
174 let state = self.state.lock().await;
175 Ok(state.settings.default_params.clone())
176 }
177
178 pub async fn set_config_gen_params(&self, request: ConfigGenParamsRequest) -> ApiResult<()> {
182 self.consensus_config_gen_params(&request).await?;
183 let mut state = self
184 .require_status(ServerStatus::SharingConfigGenParams)
185 .await?;
186 state.requested_params = Some(request);
187 info!(
188 target: fedimint_logging::LOG_NET_PEER_DKG,
189 "Set params for config gen"
190 );
191 Ok(())
192 }
193
194 async fn get_requested_params(&self) -> ApiResult<ConfigGenParamsRequest> {
195 let state = self.state.lock().await.clone();
196 state.requested_params.ok_or(ApiError::bad_request(
197 "Config params were not set on this guardian".to_string(),
198 ))
199 }
200
201 pub async fn consensus_config_gen_params(
203 &self,
204 request: &ConfigGenParamsRequest,
205 ) -> ApiResult<ConfigGenParamsResponse> {
206 let state = self.state.lock().await.clone();
207 let local = state.local.clone();
208
209 let consensus = match local.and_then(|local| local.leader_api_url) {
210 Some(leader_url) => {
211 let client = DynGlobalApi::from_pre_peer_id_admin_endpoint(
212 leader_url.clone(),
213 &self.api_secret,
214 );
215 let response = client.consensus_config_gen_params().await;
216 response
217 .map_err(|_| ApiError::not_found("Cannot get leader params".to_string()))?
218 .consensus
219 }
220 None => ConfigGenParamsConsensus {
221 peers: state.get_peer_info(),
222 meta: request.meta.clone(),
223 modules: request.modules.clone(),
224 },
225 };
226
227 let params = state.get_config_gen_params(request, consensus.clone())?;
228 Ok(ConfigGenParamsResponse {
229 consensus,
230 our_current_id: params.local.our_id,
231 })
232 }
233
234 pub async fn run_dkg(&self) -> ApiResult<()> {
242 let leader = {
243 let mut state = self
244 .require_status(ServerStatus::SharingConfigGenParams)
245 .await?;
246 state.status = ServerStatus::ReadyForConfigGen;
248 info!(
249 target: fedimint_logging::LOG_NET_PEER_DKG,
250 "Update config gen status to 'Ready for config gen'"
251 );
252 state.local.clone().and_then(|local| {
254 local.leader_api_url.map(|url| {
255 DynGlobalApi::from_pre_peer_id_admin_endpoint(url, &self.api_secret.clone())
256 })
257 })
258 };
259
260 self.update_leader().await?;
261
262 let self_clone = self.clone();
263 let sub_group = self.task_group.make_subgroup();
264 let p2p_bind_addr = self.p2p_bind_addr;
265 sub_group.spawn("run dkg", move |_handle| async move {
266 if let Some(client) = leader {
268 loop {
269 let status = client.status().await.map_err(|_| {
270 ApiError::not_found("Unable to connect to the leader".to_string())
271 })?;
272 if status.server == ServerStatus::ReadyForConfigGen {
273 break;
274 }
275 sleep(Duration::from_millis(100)).await;
276 }
277 };
278
279 let request = self_clone.get_requested_params().await?;
281 let response = self_clone.consensus_config_gen_params(&request).await?;
282 let (params, registry) = {
283 let state: MutexGuard<'_, ConfigGenState> = self_clone
284 .require_status(ServerStatus::ReadyForConfigGen)
285 .await?;
286 let params = state.get_config_gen_params(&request, response.consensus)?;
287 let registry = state.settings.registry.clone();
288 (params, registry)
289 };
290
291 let task_group: TaskGroup = self_clone.task_group.make_subgroup();
293 let config = ServerConfig::distributed_gen(
294 p2p_bind_addr,
295 ¶ms,
296 registry,
297 &task_group,
298 self_clone.code_version_str.clone(),
299 )
300 .await;
301 task_group
302 .shutdown_join_all(None)
303 .await
304 .expect("shuts down");
305
306 {
307 let mut state = self_clone.state.lock().await;
308 match config {
309 Ok(config) => {
310 state.status = ServerStatus::VerifyingConfigs;
311 state.config = Some(config);
312 info!(
313 target: fedimint_logging::LOG_NET_PEER_DKG,
314 "Set config for config gen"
315 );
316 }
317 Err(e) => {
318 error!(
319 target: fedimint_logging::LOG_NET_PEER_DKG,
320 "DKG failed with {:?}", e
321 );
322 state.status = ServerStatus::ConfigGenFailed;
323 info!(
324 target: fedimint_logging::LOG_NET_PEER_DKG,
325 "Update config gen status to 'Config gen failed'"
326 );
327 }
328 }
329 }
330 self_clone.update_leader().await
331 });
332
333 Ok(())
334 }
335
336 pub async fn verify_config_hash(&self) -> ApiResult<BTreeMap<PeerId, sha256::Hash>> {
341 let expected_status = [
342 ServerStatus::VerifyingConfigs,
343 ServerStatus::VerifiedConfigs,
344 ];
345
346 let state = self.require_any_status(&expected_status).await?;
347
348 let config = state
349 .config
350 .clone()
351 .ok_or(ApiError::bad_request("Missing config".to_string()))?;
352
353 let verification_hashes = config
354 .consensus
355 .api_endpoints
356 .keys()
357 .map(|peer| (*peer, (*peer, config.consensus.clone()).consensus_hash()))
358 .collect();
359
360 Ok(verification_hashes)
361 }
362
363 pub async fn verified_configs(&self) -> ApiResult<()> {
365 {
366 let expected_status = [
367 ServerStatus::VerifyingConfigs,
368 ServerStatus::VerifiedConfigs,
369 ];
370 let mut state = self.require_any_status(&expected_status).await?;
371 if state.status == ServerStatus::VerifiedConfigs {
372 return Ok(());
373 }
374 state.status = ServerStatus::VerifiedConfigs;
375 info!(
376 target: fedimint_logging::LOG_NET_PEER_DKG,
377 "Update config gen status to 'Verified configs'"
378 );
379 }
380
381 self.update_leader().await?;
382 Ok(())
383 }
384
385 pub async fn start_consensus(&self) -> ApiResult<()> {
386 let state = self
387 .require_any_status(&[
388 ServerStatus::VerifyingConfigs,
389 ServerStatus::VerifiedConfigs,
390 ])
391 .await?;
392
393 self.config_generated_tx
394 .send(state.config.clone().expect("Config should exist"))
395 .await
396 .expect("Can send");
397
398 Ok(())
399 }
400
401 pub async fn server_status(&self) -> ServerStatus {
403 self.state.lock().await.status.clone()
404 }
405
406 fn bad_request<T>(msg: &str) -> ApiResult<T> {
407 Err(ApiError::bad_request(msg.to_string()))
408 }
409
410 pub async fn restart_federation_setup(&self) -> ApiResult<()> {
411 let leader = {
412 let expected_status = [
413 ServerStatus::SharingConfigGenParams,
414 ServerStatus::ReadyForConfigGen,
415 ServerStatus::ConfigGenFailed,
416 ServerStatus::VerifyingConfigs,
417 ServerStatus::VerifiedConfigs,
418 ];
419 let mut state = self.require_any_status(&expected_status).await?;
420
421 state.status = ServerStatus::SetupRestarted;
422 info!(
423 target: fedimint_logging::LOG_NET_PEER_DKG,
424 "Update config gen status to 'Setup restarted'"
425 );
426 state.local.clone().and_then(|local| {
428 local
429 .leader_api_url
430 .map(|url| DynGlobalApi::from_pre_peer_id_admin_endpoint(url, &self.api_secret))
431 })
432 };
433
434 self.update_leader().await?;
435
436 let self_clone = self.clone();
439 let sub_group = self.task_group.make_subgroup();
440 sub_group.spawn("restart", |_handle| async move {
441 if let Some(client) = leader {
442 self_clone.await_leader_restart(&client).await?;
443 } else {
444 self_clone.await_peer_restart().await;
445 }
446 {
448 let mut state = self_clone.state.lock().await;
449 state.reset();
450 }
451 self_clone.update_leader().await
452 });
453
454 Ok(())
455 }
456
457 async fn await_leader_restart(&self, client: &DynGlobalApi) -> ApiResult<()> {
459 let mut retries = 0;
460 loop {
461 if let Ok(status) = client.status().await {
462 if status.server == ServerStatus::AwaitingPassword
463 || status.server == ServerStatus::SharingConfigGenParams
464 {
465 break Ok(());
466 }
467 } else {
468 if retries > 3 {
469 return Err(ApiError::not_found(
470 "Unable to connect to the leader".to_string(),
471 ));
472 }
473 retries += 1;
474 }
475 sleep(Duration::from_millis(100)).await;
476 }
477 }
478
479 async fn await_peer_restart(&self) {
481 loop {
482 {
483 let state = self.state.lock().await;
484 let peers = state.peers.clone();
485 if peers
486 .values()
487 .all(|peer| peer.status == Some(ServerStatus::SetupRestarted))
488 {
489 break;
490 }
491 }
492 sleep(Duration::from_millis(100)).await;
493 }
494 }
495
496 pub async fn check_bitcoin_status(&self) -> ApiResult<BitcoinRpcConnectionStatus> {
498 {
500 let cached_status = self.bitcoin_status_cache.read().await;
501 if let Some((timestamp, status)) = cached_status.as_ref() {
502 if timestamp.elapsed() < self.bitcoin_status_cache_duration {
503 return Ok(*status);
504 }
505 }
506 }
507
508 let status = Self::fetch_bitcoin_status().await?;
510
511 let mut cached_status = self.bitcoin_status_cache.write().await;
513 *cached_status = Some((Instant::now(), status));
514
515 Ok(status)
516 }
517
518 async fn fetch_bitcoin_status() -> ApiResult<BitcoinRpcConnectionStatus> {
519 let bitcoin_rpc_config = BitcoinRpcConfig::get_defaults_from_env_vars().map_err(|e| {
520 ApiError::server_error(format!("Failed to get bitcoin rpc env vars: {e}"))
521 })?;
522 let client = create_bitcoind(&bitcoin_rpc_config).map_err(|e| {
523 ApiError::server_error(format!("Failed to connect to bitcoin rpc: {e}"))
524 })?;
525 let block_count = client.get_block_count().await.map_err(|e| {
526 ApiError::server_error(format!("Failed to get block count from bitcoin rpc: {e}"))
527 })?;
528 let chain_tip_block_height = block_count - 1;
529 let chain_tip_block_hash = client
530 .get_block_hash(chain_tip_block_height)
531 .await
532 .map_err(|e| {
533 ApiError::server_error(format!(
534 "Failed to get block hash for block count {block_count} from bitcoin rpc: {e}"
535 ))
536 })?;
537 let chain_tip_block = client.get_block(&chain_tip_block_hash).await.map_err(|e| {
538 ApiError::server_error(format!(
539 "Failed to get block for block hash {chain_tip_block_hash} from bitcoin rpc: {e}"
540 ))
541 })?;
542 let chain_tip_block_time = chain_tip_block.header.time;
543 let sync_percentage = client.get_sync_percentage().await.map_err(|e| {
544 ApiError::server_error(format!(
545 "Failed to get sync percentage from bitcoin rpc: {e}"
546 ))
547 })?;
548
549 Ok(BitcoinRpcConnectionStatus {
550 chain_tip_block_height,
551 chain_tip_block_time,
552 sync_percentage,
553 })
554 }
555}
556
557#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
558pub struct BitcoinRpcConnectionStatus {
559 chain_tip_block_height: u64,
560 chain_tip_block_time: u32,
561 sync_percentage: Option<f64>,
562}
563
564#[derive(Debug, Clone)]
566pub struct ConfigGenParamsLocal {
567 pub our_id: PeerId,
569 pub our_private_key: rustls::PrivateKey,
571 pub api_auth: ApiAuth,
573 pub p2p_bind: SocketAddr,
575 pub api_bind: SocketAddr,
577 pub max_connections: u32,
579}
580
581#[derive(Debug, Clone)]
583pub struct ConfigGenSettings {
584 pub download_token_limit: Option<u64>,
586 pub p2p_bind: SocketAddr,
588 pub api_bind: SocketAddr,
590 pub p2p_url: SafeUrl,
592 pub api_url: SafeUrl,
594 pub default_params: ConfigGenParamsRequest,
596 pub max_connections: u32,
598 pub registry: ServerModuleInitRegistry,
600}
601
602#[derive(Debug, Clone)]
604pub struct ConfigGenState {
605 settings: ConfigGenSettings,
607 auth: Option<ApiAuth>,
609 local: Option<ConfigGenLocalConnection>,
611 peers: BTreeMap<SafeUrl, PeerServerParams>,
614 requested_params: Option<ConfigGenParamsRequest>,
616 status: ServerStatus,
618 config: Option<ServerConfig>,
620}
621
622#[derive(Debug, Clone)]
624struct ConfigGenLocalConnection {
625 tls_private: rustls::PrivateKey,
627 tls_cert: rustls::Certificate,
629 our_name: String,
631 leader_api_url: Option<SafeUrl>,
634}
635
636impl ConfigGenState {
637 fn new(settings: ConfigGenSettings) -> Self {
638 Self {
639 settings,
640 auth: None,
641 local: None,
642 peers: BTreeMap::new(),
643 requested_params: None,
644 status: ServerStatus::AwaitingPassword,
645 config: None,
646 }
647 }
648
649 fn set_request(&mut self, request: ConfigGenConnectionsRequest) -> ApiResult<()> {
650 let (tls_cert, tls_private) = gen_cert_and_key(&request.our_name)
651 .map_err(|_| ApiError::server_error("Unable to generate TLS keys".to_string()))?;
652 self.local = Some(ConfigGenLocalConnection {
653 tls_private,
654 tls_cert,
655 our_name: request.our_name,
656 leader_api_url: request.leader_api_url,
657 });
658 info!(
659 target: fedimint_logging::LOG_NET_PEER_DKG,
660 "Set local connection for config gen"
661 );
662 Ok(())
663 }
664
665 fn local_connection(&self) -> ApiResult<ConfigGenLocalConnection> {
666 self.local.clone().ok_or(ApiError::bad_request(
667 "Our connection info not set yet".to_string(),
668 ))
669 }
670
671 fn auth(&self) -> ApiResult<ApiAuth> {
672 self.auth
673 .clone()
674 .ok_or(ApiError::bad_request("Missing auth".to_string()))
675 }
676
677 fn our_peer_info(&self) -> ApiResult<PeerServerParams> {
678 let local = self.local_connection()?;
679 Ok(PeerServerParams {
680 cert: local.tls_cert.clone(),
681 p2p_url: self.settings.p2p_url.clone(),
682 api_url: self.settings.api_url.clone(),
683 name: local.our_name,
684 status: Some(self.status.clone()),
685 })
686 }
687
688 fn get_peer_info(&self) -> BTreeMap<PeerId, PeerServerParams> {
689 self.peers
690 .values()
691 .cloned()
692 .chain(self.our_peer_info().ok())
693 .sorted_by_cached_key(|peer| {
697 if std::env::var_os(FM_PEER_ID_SORT_BY_URL_ENV).is_some_and(|var| !var.is_empty()) {
700 peer.api_url.to_string()
701 } else {
702 peer.name.to_lowercase()
703 }
704 })
705 .enumerate()
706 .map(|(i, peer)| (PeerId::from(i as u16), peer))
707 .collect()
708 }
709
710 fn get_config_gen_params(
713 &self,
714 request: &ConfigGenParamsRequest,
715 mut consensus: ConfigGenParamsConsensus,
716 ) -> ApiResult<ConfigGenParams> {
717 let local_connection = self.local_connection()?;
718
719 let (our_id, _) = consensus
720 .peers
721 .iter()
722 .find(|(_, param)| local_connection.tls_cert == param.cert)
723 .ok_or(ApiError::bad_request(
724 "Our TLS cert not found among peers".to_string(),
725 ))?;
726
727 let mut combined_params = vec![];
728 let default_params = self.settings.default_params.modules.clone();
729 let local_params = request.modules.clone();
730 let consensus_params = consensus.modules.clone();
731 for (id, kind, default) in default_params.iter_modules() {
733 let consensus = &consensus_params.get(id).unwrap_or(default).consensus;
734 let local = &local_params.get(id).unwrap_or(default).local;
735 let combined = ConfigGenModuleParams::new(local.clone(), consensus.clone());
736 let module = self.settings.registry.get(kind).expect("Module exists");
738 module.validate_params(&combined).map_err(|e| {
739 ApiError::bad_request(format!(
740 "Module {} params invalid: {}",
741 id,
742 itertools::join(e.chain(), ": ")
743 ))
744 })?;
745 combined_params.push((id, kind.clone(), combined));
746 }
747 consensus.modules = ServerModuleConfigGenParamsRegistry::from_iter(combined_params);
748
749 let local = ConfigGenParamsLocal {
750 our_id: *our_id,
751 our_private_key: local_connection.tls_private,
752 api_auth: self.auth()?,
753 p2p_bind: self.settings.p2p_bind,
754 api_bind: self.settings.api_bind,
755 max_connections: self.settings.max_connections,
756 };
757
758 Ok(ConfigGenParams { local, consensus })
759 }
760
761 fn reset(&mut self) {
762 self.auth = None;
763 self.local = None;
764 self.peers = BTreeMap::new();
765 self.requested_params = None;
766 self.status = ServerStatus::AwaitingPassword;
767 self.config = None;
768
769 info!(
770 target: fedimint_logging::LOG_NET_PEER_DKG,
771 "Reset config gen state"
772 );
773 }
774}
775
776#[async_trait]
777impl HasApiContext<ConfigGenApi> for ConfigGenApi {
778 async fn context(
779 &self,
780 request: &ApiRequestErased,
781 id: Option<ModuleInstanceId>,
782 ) -> (&ConfigGenApi, ApiEndpointContext<'_>) {
783 let mut db = self.db.clone();
784 let mut dbtx = self.db.begin_transaction().await;
785 if let Some(id) = id {
786 db = self.db.with_prefix_module_id(id).0;
787 dbtx = dbtx.with_prefix_module_id(id).0;
788 }
789 let state = self.state.lock().await;
790 let auth = request.auth.as_ref();
791 let has_auth = match state.auth.clone() {
792 None => true,
794 Some(configured_auth) => Some(&configured_auth) == auth,
795 };
796
797 (
798 self,
799 ApiEndpointContext::new(db, dbtx, has_auth, request.auth.clone()),
800 )
801 }
802}
803
804pub fn server_endpoints() -> Vec<ApiEndpoint<ConfigGenApi>> {
805 vec![
806 api_endpoint! {
807 SET_PASSWORD_ENDPOINT,
808 ApiVersion::new(0, 0),
809 async |config: &ConfigGenApi, context, _v: ()| -> () {
810 match context.request_auth() {
811 None => return Err(ApiError::bad_request("Missing password".to_string())),
812 Some(auth) => config.set_password(auth).await
813 }
814 }
815 },
816 api_endpoint! {
817 SET_CONFIG_GEN_CONNECTIONS_ENDPOINT,
818 ApiVersion::new(0, 0),
819 async |config: &ConfigGenApi, context, server: ConfigGenConnectionsRequest| -> () {
820 check_auth(context)?;
821 config.set_config_gen_connections(server).await
822 }
823 },
824 api_endpoint! {
825 ADD_CONFIG_GEN_PEER_ENDPOINT,
826 ApiVersion::new(0, 0),
827 async |config: &ConfigGenApi, _context, peer: PeerServerParams| -> () {
828 config.add_config_gen_peer(peer).await
830 }
831 },
832 api_endpoint! {
833 CONFIG_GEN_PEERS_ENDPOINT,
834 ApiVersion::new(0, 0),
835 async |config: &ConfigGenApi, _context, _v: ()| -> Vec<PeerServerParams> {
836 config.config_gen_peers().await
837 }
838 },
839 api_endpoint! {
840 DEFAULT_CONFIG_GEN_PARAMS_ENDPOINT,
841 ApiVersion::new(0, 0),
842 async |config: &ConfigGenApi, context, _v: ()| -> ConfigGenParamsRequest {
843 check_auth(context)?;
844 config.default_config_gen_params().await
845 }
846 },
847 api_endpoint! {
848 SET_CONFIG_GEN_PARAMS_ENDPOINT,
849 ApiVersion::new(0, 0),
850 async |config: &ConfigGenApi, context, params: ConfigGenParamsRequest| -> () {
851 check_auth(context)?;
852 config.set_config_gen_params(params).await
853 }
854 },
855 api_endpoint! {
856 CONSENSUS_CONFIG_GEN_PARAMS_ENDPOINT,
857 ApiVersion::new(0, 0),
858 async |config: &ConfigGenApi, _context, _v: ()| -> ConfigGenParamsResponse {
859 let request = config.get_requested_params().await?;
860 config.consensus_config_gen_params(&request).await
861 }
862 },
863 api_endpoint! {
864 RUN_DKG_ENDPOINT,
865 ApiVersion::new(0, 0),
866 async |config: &ConfigGenApi, context, _v: ()| -> () {
867 check_auth(context)?;
868 config.run_dkg().await
869 }
870 },
871 api_endpoint! {
872 VERIFY_CONFIG_HASH_ENDPOINT,
873 ApiVersion::new(0, 0),
874 async |config: &ConfigGenApi, context, _v: ()| -> BTreeMap<PeerId, sha256::Hash> {
875 check_auth(context)?;
876 config.verify_config_hash().await
877 }
878 },
879 api_endpoint! {
880 VERIFIED_CONFIGS_ENDPOINT,
881 ApiVersion::new(0, 0),
882 async |config: &ConfigGenApi, context, _v: ()| -> () {
883 check_auth(context)?;
884 config.verified_configs().await
885 }
886 },
887 api_endpoint! {
888 START_CONSENSUS_ENDPOINT,
889 ApiVersion::new(0, 0),
890 async |config: &ConfigGenApi, context, _v: ()| -> () {
891 check_auth(context)?;
892 config.start_consensus().await
893 }
894 },
895 api_endpoint! {
896 STATUS_ENDPOINT,
897 ApiVersion::new(0, 0),
898 async |config: &ConfigGenApi, _context, _v: ()| -> StatusResponse {
899 let server = config.server_status().await;
900 Ok(StatusResponse {
901 server,
902 federation: None
903 })
904 }
905 },
906 api_endpoint! {
907 AUTH_ENDPOINT,
908 ApiVersion::new(0, 0),
909 async |_config: &ConfigGenApi, context, _v: ()| -> () {
910 check_auth(context)?;
911 Ok(())
912 }
913 },
914 api_endpoint! {
915 RESTART_FEDERATION_SETUP_ENDPOINT,
916 ApiVersion::new(0, 0),
917 async |config: &ConfigGenApi, context, _v: ()| -> () {
918 check_auth(context)?;
919 config.restart_federation_setup().await
920 }
921 },
922 api_endpoint! {
923 CHECK_BITCOIN_STATUS_ENDPOINT,
924 ApiVersion::new(0, 0),
925 async |config: &ConfigGenApi, context, _v: ()| -> BitcoinRpcConnectionStatus {
926 check_auth(context)?;
927 config.check_bitcoin_status().await
928 }
929 },
930 ]
931}