Skip to content

Commit

Permalink
Get federation config from URL if available
Browse files Browse the repository at this point in the history
  • Loading branch information
TonyGiorgio committed May 7, 2024
1 parent f85e8d4 commit ae520d3
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 41 deletions.
206 changes: 166 additions & 40 deletions mutiny-core/src/federation.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::utils::{convert_from_fedimint_invoice, convert_to_fedimint_invoice, now, spawn};
use crate::utils::{
convert_from_fedimint_invoice, convert_to_fedimint_invoice, fetch_with_timeout, now, spawn,
};
use crate::{
error::{MutinyError, MutinyStorageError},
event::PaymentInfo,
Expand Down Expand Up @@ -56,6 +58,7 @@ use futures_util::{pin_mut, StreamExt};
use hex_conservative::{DisplayHex, FromHex};
use lightning::{log_debug, log_error, log_info, log_trace, log_warn, util::logger::Logger};
use lightning_invoice::Bolt11Invoice;
use reqwest::Method;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;
Expand Down Expand Up @@ -162,6 +165,39 @@ pub struct FederationIdentity {
pub social_recovery_disabled: Option<bool>,
}

#[derive(Serialize, Deserialize)]
struct FederationMetaConfig {
#[serde(flatten)]
pub federations: std::collections::HashMap<String, FederationMeta>,
}

// This is the FederationUrlConfig that refer to a specific federation
// Normal config information that might exist from their URL.
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq)]
pub struct FederationMeta {
// https://github.com/fedimint/fedimint/tree/master/docs/meta_fields
pub federation_name: Option<String>,
pub federation_expiry_timestamp: Option<String>,
pub welcome_message: Option<String>,
pub gateway_fees: Option<GatewayFees>,
// undocumented parameters that fedi uses: https://meta.dev.fedibtc.com/meta.json
pub default_currency: Option<String>,
pub federation_icon_url: Option<String>,
pub max_balance_msats: Option<u32>,
pub max_invoice_msats: Option<u32>,
pub meta_external_url: Option<String>,
pub onchain_deposits_disabled: Option<bool>,
pub preview_message: Option<String>,
pub sites: Option<Site>,
pub public: Option<bool>,
pub tos_url: Option<String>,
pub popup_end_timestamp: Option<u32>,
pub popup_countdown_message: Option<String>,
pub invite_codes_disabled: Option<bool>,
pub stability_pool_disabled: Option<bool>,
pub social_recovery_disabled: Option<bool>,
}

#[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Site {
pub id: Option<String>,
Expand Down Expand Up @@ -688,6 +724,7 @@ impl<S: MutinyStorage> FederationClient<S> {
self.invite_code.clone(),
gateway_fees,
)
.await
}

// delete_fedimint_storage is not suggested at the moment due to the lack of easy restores
Expand All @@ -697,53 +734,142 @@ impl<S: MutinyStorage> FederationClient<S> {
}
}

pub(crate) fn get_federation_identity(
pub(crate) async fn get_federation_identity(
uuid: String,
fedimint_client: ClientHandleArc,
invite_code: InviteCode,
gateway_fees: Option<GatewayFees>,
) -> FederationIdentity {
let federation_id = fedimint_client.federation_id();
let meta_external_url = fedimint_client.get_meta("meta_external_url");
let config = if let Some(ref url) = meta_external_url {
let http_client = reqwest::Client::new();
let request = http_client.request(Method::GET, url);

match fetch_with_timeout(&http_client, request.build().expect("should build req")).await {
Ok(r) => match r.json::<FederationMetaConfig>().await {
Ok(c) =>
{
#[allow(clippy::map_clone)]
c.federations
.get(&federation_id.to_string())
.map(|f| f.clone())
}
Err(_) => None,
},
Err(_) => None,
}
} else {
None
};

FederationIdentity {
uuid: uuid.clone(),
federation_id: fedimint_client.federation_id(),
federation_id,
invite_code: invite_code.clone(),
federation_name: fedimint_client.get_meta("federation_name"),
federation_expiry_timestamp: fedimint_client.get_meta("federation_expiry_timestamp"),
welcome_message: fedimint_client.get_meta("welcome_message"),
gateway_fees,
default_currency: fedimint_client.get_meta("default_currency"),
federation_icon_url: fedimint_client.get_meta("federation_icon_url"),
max_balance_msats: fedimint_client
.get_meta("max_balance_msats")
.map(|v| v.parse().unwrap_or(0)),
max_invoice_msats: fedimint_client
.get_meta("max_invoice_msats")
.map(|v| v.parse().unwrap_or(0)),
meta_external_url: fedimint_client.get_meta("meta_external_url"),
onchain_deposits_disabled: fedimint_client
.get_meta("onchain_deposits_disabled")
.map(|v| v.parse().unwrap_or(false)),
preview_message: fedimint_client.get_meta("preview_message"),
sites: fedimint_client
.get_meta("sites")
.map(|v| serde_json::from_str(&v).unwrap_or_default()),
public: fedimint_client
.get_meta("public")
.map(|v| v.parse().unwrap_or(false)),
tos_url: fedimint_client.get_meta("tos_url"),
popup_end_timestamp: fedimint_client
.get_meta("popup_end_timestamp")
.map(|v| v.parse().unwrap_or(0)),
popup_countdown_message: fedimint_client.get_meta("popup_countdown_message"),
invite_codes_disabled: fedimint_client
.get_meta("invite_codes_disabled")
.map(|v| v.parse().unwrap_or(false)),
stability_pool_disabled: fedimint_client
.get_meta("stability_pool_disabled")
.map(|v| v.parse().unwrap_or(false)),
social_recovery_disabled: fedimint_client
.get_meta("social_recovery_disabled")
.map(|v| v.parse().unwrap_or(false)),
federation_name: merge_values(
fedimint_client.get_meta("federation_name").clone(),
config.as_ref().and_then(|c| c.federation_name.clone()),
),
federation_expiry_timestamp: merge_values(
fedimint_client.get_meta("federation_expiry_timestamp"),
config
.as_ref()
.and_then(|c| c.federation_expiry_timestamp.clone()),
),
welcome_message: merge_values(
fedimint_client.get_meta("welcome_message"),
config.as_ref().and_then(|c| c.welcome_message.clone()),
),
gateway_fees, // Already merged using helper function...
default_currency: merge_values(
fedimint_client.get_meta("default_currency"),
config.as_ref().and_then(|c| c.default_currency.clone()),
),
federation_icon_url: merge_values(
fedimint_client.get_meta("federation_icon_url"),
config.as_ref().and_then(|c| c.federation_icon_url.clone()),
),
max_balance_msats: merge_values(
fedimint_client
.get_meta("max_balance_msats")
.map(|v| v.parse().unwrap_or(0)),
config.as_ref().and_then(|c| c.max_balance_msats),
),
max_invoice_msats: merge_values(
fedimint_client
.get_meta("max_invoice_msats")
.map(|v| v.parse().unwrap_or(0)),
config.as_ref().and_then(|c| c.max_invoice_msats),
),
meta_external_url, // Already set...
onchain_deposits_disabled: merge_values(
fedimint_client
.get_meta("onchain_deposits_disabled")
.map(|v| v.parse().unwrap_or(false)),
config.as_ref().and_then(|c| c.onchain_deposits_disabled),
),
preview_message: merge_values(
fedimint_client.get_meta("preview_message"),
config.as_ref().and_then(|c| c.preview_message.clone()),
),
sites: merge_values(
fedimint_client
.get_meta("sites")
.map(|v| serde_json::from_str(&v).unwrap_or_default()),
config.as_ref().and_then(|c| c.sites.clone()),
),
public: merge_values(
fedimint_client
.get_meta("public")
.map(|v| v.parse().unwrap_or(false)),
config.as_ref().and_then(|c| c.public),
),
tos_url: merge_values(
fedimint_client.get_meta("tos_url"),
config.as_ref().and_then(|c| c.tos_url.clone()),
),
popup_end_timestamp: merge_values(
fedimint_client
.get_meta("popup_end_timestamp")
.map(|v| v.parse().unwrap_or(0)),
config.as_ref().and_then(|c| c.popup_end_timestamp),
),
popup_countdown_message: merge_values(
fedimint_client
.get_meta("popup_countdown_message")
.map(|v| v.to_string()),
config
.as_ref()
.and_then(|c| c.popup_countdown_message.clone()),
),
invite_codes_disabled: merge_values(
fedimint_client
.get_meta("invite_codes_disabled")
.map(|v| v.parse().unwrap_or(false)),
config.as_ref().and_then(|c| c.invite_codes_disabled),
),
stability_pool_disabled: merge_values(
fedimint_client
.get_meta("stability_pool_disabled")
.map(|v| v.parse().unwrap_or(false)),
config.as_ref().and_then(|c| c.stability_pool_disabled),
),
social_recovery_disabled: merge_values(
fedimint_client
.get_meta("social_recovery_disabled")
.map(|v| v.parse().unwrap_or(false)),
config.as_ref().and_then(|c| c.social_recovery_disabled),
),
}
}

fn merge_values<T>(a: Option<T>, b: Option<T>) -> Option<T> {
match (a, b) {
// If a has value return that; otherwise, use the one from b if available.
(Some(val), _) => Some(val),
(None, Some(val)) => Some(val),
(None, None) => None,
}
}

Expand Down
3 changes: 2 additions & 1 deletion mutiny-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3201,7 +3201,8 @@ pub(crate) async fn create_new_federation<S: MutinyStorage>(
new_federation.fedimint_client.clone(),
federation_code.clone(),
gateway_fees,
);
)
.await;

federations
.write()
Expand Down

0 comments on commit ae520d3

Please sign in to comment.