fedimint_api_client/
lib.rs1#![deny(clippy::pedantic)]
2#![allow(clippy::missing_errors_doc)]
3#![allow(clippy::missing_panics_doc)]
4#![allow(clippy::module_name_repetitions)]
5#![allow(clippy::must_use_candidate)]
6#![allow(clippy::return_self_not_must_use)]
7
8use anyhow::{bail, Context as _};
9use api::net::Connector;
10use api::{DynGlobalApi, FederationApiExt as _, WsFederationApi};
11use fedimint_core::config::{ClientConfig, FederationId};
12use fedimint_core::endpoint_constants::CLIENT_CONFIG_ENDPOINT;
13use fedimint_core::invite_code::InviteCode;
14use fedimint_core::module::ApiRequestErased;
15use fedimint_core::util::backoff_util;
16use fedimint_core::NumPeers;
17use query::FilterMap;
18use tracing::debug;
19
20pub mod api;
21pub mod query;
23
24impl Connector {
25 pub async fn download_from_invite_code(
29 &self,
30 invite_code: &InviteCode,
31 ) -> anyhow::Result<ClientConfig> {
32 debug!("Downloading client config from {:?}", invite_code);
33
34 let federation_id = invite_code.federation_id();
35 let api = DynGlobalApi::from_invite_code(self, invite_code);
36 let api_secret = invite_code.api_secret();
37
38 fedimint_core::util::retry(
39 "Downloading client config",
40 backoff_util::aggressive_backoff(),
41 || self.try_download_client_config(&api, federation_id, api_secret.clone()),
42 )
43 .await
44 .context("Failed to download client config")
45 }
46
47 pub async fn try_download_client_config(
49 &self,
50 api: &DynGlobalApi,
51 federation_id: FederationId,
52 api_secret: Option<String>,
53 ) -> anyhow::Result<ClientConfig> {
54 let query_strategy = FilterMap::new(
56 move |cfg: ClientConfig| {
57 if federation_id != cfg.global.calculate_federation_id() {
58 bail!("FederationId in invite code does not match client config")
59 }
60
61 Ok(cfg.global.api_endpoints)
62 },
63 NumPeers::from(1),
64 );
65
66 let api_endpoints = api
67 .request_with_strategy(
68 query_strategy,
69 CLIENT_CONFIG_ENDPOINT.to_owned(),
70 ApiRequestErased::default(),
71 )
72 .await?;
73
74 let api_endpoints = api_endpoints.into_iter().map(|(peer, url)| (peer, url.url));
76
77 let client_config = WsFederationApi::new(self, api_endpoints, &api_secret)
78 .request_current_consensus::<ClientConfig>(
79 CLIENT_CONFIG_ENDPOINT.to_owned(),
80 ApiRequestErased::default(),
81 )
82 .await?;
83
84 if client_config.calculate_federation_id() != federation_id {
85 bail!("Obtained client config has different federation id");
86 }
87
88 Ok(client_config)
89 }
90}