fedimint_api_client/
lib.rs#![deny(clippy::pedantic)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::return_self_not_must_use)]
use anyhow::{bail, Context as _};
use api::net::Connector;
use api::{DynGlobalApi, FederationApiExt as _, PeerError};
use fedimint_core::config::{ClientConfig, FederationId};
use fedimint_core::endpoint_constants::CLIENT_CONFIG_ENDPOINT;
use fedimint_core::invite_code::InviteCode;
use fedimint_core::module::ApiRequestErased;
use fedimint_core::util::backoff_util;
use fedimint_logging::LOG_CLIENT;
use query::FilterMap;
use tracing::debug;
pub mod api;
pub mod query;
impl Connector {
pub async fn download_from_invite_code(
&self,
invite: &InviteCode,
) -> anyhow::Result<ClientConfig> {
debug!(target: LOG_CLIENT, %invite, "Downloading client config via invite code");
let federation_id = invite.federation_id();
let api = DynGlobalApi::from_endpoints(invite.peers(), &invite.api_secret(), self);
let api_secret = invite.api_secret();
fedimint_core::util::retry(
"Downloading client config",
backoff_util::aggressive_backoff(),
|| self.try_download_client_config(&api, federation_id, api_secret.clone()),
)
.await
.context("Failed to download client config")
}
pub async fn try_download_client_config(
&self,
api: &DynGlobalApi,
federation_id: FederationId,
api_secret: Option<String>,
) -> anyhow::Result<ClientConfig> {
debug!(target: LOG_CLIENT, "Downloading client config from peer");
let query_strategy = FilterMap::new(move |cfg: ClientConfig| {
if federation_id != cfg.global.calculate_federation_id() {
return Err(PeerError::ConditionFailed(anyhow::anyhow!(
"FederationId in invite code does not match client config"
)));
}
Ok(cfg.global.api_endpoints)
});
let api_endpoints = api
.request_with_strategy(
query_strategy,
CLIENT_CONFIG_ENDPOINT.to_owned(),
ApiRequestErased::default(),
)
.await?;
let api_endpoints = api_endpoints.into_iter().map(|(peer, url)| (peer, url.url));
debug!(target: LOG_CLIENT, "Verifying client config with all peers");
let client_config = DynGlobalApi::from_endpoints(api_endpoints, &api_secret, self)
.request_current_consensus::<ClientConfig>(
CLIENT_CONFIG_ENDPOINT.to_owned(),
ApiRequestErased::default(),
)
.await?;
if client_config.calculate_federation_id() != federation_id {
bail!("Obtained client config has different federation id");
}
Ok(client_config)
}
}