Skip to content
This repository has been archived by the owner on Feb 3, 2025. It is now read-only.

Commit

Permalink
Merge pull request #1017 from MutinyWallet/final-startup
Browse files Browse the repository at this point in the history
Start up optimizations grab bag
  • Loading branch information
benthecarman authored Feb 12, 2024
2 parents 2cd9433 + 3e6df91 commit 2874b2d
Show file tree
Hide file tree
Showing 10 changed files with 410 additions and 173 deletions.
20 changes: 13 additions & 7 deletions mutiny-core/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ use crate::{
networking::websocket::{SimpleWebSocket, WebSocketImpl},
utils,
};
use async_lock::RwLock;
use jwt_compact::UntrustedToken;
use lightning::util::logger::*;
use lightning::{log_error, log_info};
use lightning::{log_debug, log_error, log_info};
use lnurl::{lnurl::LnUrl, AsyncClient as LnUrlClient};
use reqwest::Client;
use reqwest::{Method, StatusCode, Url};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::str::FromStr;
use std::sync::{Arc, RwLock};
use std::sync::Arc;

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct CustomClaims {
Expand Down Expand Up @@ -54,8 +55,9 @@ impl MutinyAuthClient {
Ok(())
}

pub fn is_authenticated(&self) -> Option<String> {
if let Some(ref jwt) = *self.jwt.try_read().unwrap() {
pub async fn is_authenticated(&self) -> Option<String> {
let lock = self.jwt.read().await;
if let Some(jwt) = lock.as_ref() {
return Some(jwt.to_string()); // TODO parse and make sure still valid
}
None
Expand Down Expand Up @@ -92,7 +94,7 @@ impl MutinyAuthClient {
) -> Result<reqwest::Response, MutinyError> {
let mut request = self.http_client.request(method, url);

let mut jwt = self.is_authenticated();
let mut jwt = self.is_authenticated().await;
if jwt.is_none() {
jwt = Some(self.retrieve_new_jwt().await?);
}
Expand All @@ -110,6 +112,10 @@ impl MutinyAuthClient {
}

async fn retrieve_new_jwt(&self) -> Result<String, MutinyError> {
// get lock so we don't make multiple requests for the same token
let mut lock = self.jwt.write().await;
log_debug!(self.logger, "Retrieving new JWT token");

let mut url = Url::parse(&self.url).map_err(|_| MutinyError::LnUrlFailure)?;
let ws_scheme = match url.scheme() {
"http" => "ws",
Expand Down Expand Up @@ -165,7 +171,7 @@ impl MutinyAuthClient {
};

log_info!(self.logger, "Retrieved new JWT token");
*self.jwt.try_write()? = Some(jwt.clone());
*lock = Some(jwt.clone());
Ok(jwt)
}
}
Expand Down Expand Up @@ -201,7 +207,7 @@ mod tests {

// Test authenticate method
match auth_client.authenticate().await {
Ok(_) => assert!(auth_client.is_authenticated().is_some()),
Ok(_) => assert!(auth_client.is_authenticated().await.is_some()),
Err(e) => panic!("Authentication failed with error: {:?}", e),
};

Expand Down
111 changes: 62 additions & 49 deletions mutiny-core/src/federation.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::utils::spawn;
use crate::{
error::{MutinyError, MutinyStorageError},
event::PaymentInfo,
Expand Down Expand Up @@ -55,11 +56,15 @@ use fedimint_wallet_client::{WalletClientInit, WalletClientModule};
use futures::future::{self};
use futures_util::{pin_mut, StreamExt};
use hex::FromHex;
#[cfg(target_arch = "wasm32")]
use instant::Instant;
use lightning::{
ln::PaymentHash, log_debug, log_error, log_info, log_trace, log_warn, util::logger::Logger,
};
use lightning_invoice::{Bolt11Invoice, RoutingFees};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;
use std::{collections::HashMap, fmt::Debug, sync::Arc};
use std::{
str::FromStr,
Expand Down Expand Up @@ -192,35 +197,39 @@ impl<S: MutinyStorage> FederationClient<S> {
) -> Result<Self, MutinyError> {
log_info!(logger, "initializing a new federation client: {uuid}");

let federation_info = FederationInfo::from_invite_code(federation_code.clone())
.await
.map_err(|e| {
log_error!(logger, "Could not parse invite code: {}", e);
e
})?;

log_debug!(
logger,
"parsed federation invite code: {:?}",
federation_info.invite_code()
);
let federation_id = federation_code.federation_id();

let mut client_builder = fedimint_client::Client::builder();
client_builder.with_module(WalletClientInit(None));
client_builder.with_module(MintClientInit);
client_builder.with_module(LightningClientInit);

log_trace!(logger, "Building fedimint client db");
let fedimint_storage = FedimintStorage::new(
storage.clone(),
federation_info.federation_id().to_string(),
logger.clone(),
)
.await?;
let fedimint_storage =
FedimintStorage::new(storage.clone(), federation_id.to_string(), logger.clone())
.await?;
let db = fedimint_storage.clone().into();

if get_config_from_db(&db).await.is_none() {
client_builder.with_federation_info(federation_info.clone());
let download = Instant::now();
let federation_info = FederationInfo::from_invite_code(federation_code)
.await
.map_err(|e| {
log_error!(logger, "Could not parse invite code: {}", e);
e
})?;
log_trace!(
logger,
"Downloaded federation info in: {}ms",
download.elapsed().as_millis()
);

log_debug!(
logger,
"parsed federation invite code: {:?}",
federation_info.invite_code()
);
client_builder.with_federation_info(federation_info);
}

client_builder.with_database(db);
Expand All @@ -230,10 +239,7 @@ impl<S: MutinyStorage> FederationClient<S> {
let secret = create_federation_secret(xprivkey, network)?;

let fedimint_client = client_builder
.build(get_default_client_secret(
&secret,
&federation_info.federation_id(),
))
.build(get_default_client_secret(&secret, &federation_id))
.await?;

log_trace!(logger, "Retrieving fedimint wallet client module");
Expand All @@ -249,36 +255,43 @@ impl<S: MutinyStorage> FederationClient<S> {
return Err(MutinyError::NetworkMismatch);
}

// Set active gateway preference
let lightning_module = fedimint_client.get_first_module::<LightningClientModule>();
let gateways = lightning_module
.fetch_registered_gateways()
.await
.map_err(|e| {
log_warn!(
logger,
"Could not fetch gateways from federation {}: {e}",
federation_info.federation_id()
)
});

if let Ok(gateways) = gateways {
if let Some(a) = get_gateway_preference(gateways, fedimint_client.federation_id()) {
log_info!(
logger,
"Setting active gateway for federation {}: {:?}",
federation_info.federation_id(),
a
);
let _ = lightning_module.set_active_gateway(&a).await.map_err(|e| {
// Set active gateway preference in background
let client_clone = fedimint_client.clone();
let logger_clone = logger.clone();
spawn(async move {
let start = Instant::now();
let lightning_module = client_clone.get_first_module::<LightningClientModule>();
let gateways = lightning_module
.fetch_registered_gateways()
.await
.map_err(|e| {
log_warn!(
logger,
"Could not set gateway for federation {}: {e}",
federation_info.federation_id()
logger_clone,
"Could not fetch gateways from federation {federation_id}: {e}",
)
});

if let Ok(gateways) = gateways {
if let Some(a) = get_gateway_preference(gateways, federation_id) {
log_info!(
logger_clone,
"Setting active gateway for federation {federation_id}: {a}"
);
let _ = lightning_module.set_active_gateway(&a).await.map_err(|e| {
log_warn!(
logger_clone,
"Could not set gateway for federation {federation_id}: {e}",
)
});
}
}
}

log_trace!(
logger_clone,
"Setting active gateway took: {}ms",
start.elapsed().as_millis()
);
});

log_debug!(logger, "Built fedimint client");
Ok(FederationClient {
Expand Down
89 changes: 55 additions & 34 deletions mutiny-core/src/gossip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ use crate::{
};
use bitcoin::hashes::hex::{FromHex, ToHex};
use bitcoin::Network;
#[cfg(target_arch = "wasm32")]
use instant::Instant;
use lightning::ln::msgs::NodeAnnouncement;
use lightning::routing::gossip::NodeId;
use lightning::util::logger::Logger;
use lightning::util::ser::ReadableArgs;
use lightning::{log_debug, log_error, log_info, log_warn};
use lightning::{log_debug, log_error, log_info, log_trace, log_warn};
use reqwest::Client;
use reqwest::{Method, Url};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;

use crate::logging::MutinyLogger;
use crate::node::{NetworkGraph, RapidGossipSync};
Expand Down Expand Up @@ -105,7 +109,7 @@ pub struct Scorer {
pub value: String,
}

pub async fn get_remote_scorer_bytes(
async fn get_remote_scorer_bytes(
auth_client: &MutinyAuthClient,
base_url: &str,
) -> Result<Vec<u8>, MutinyError> {
Expand All @@ -126,6 +130,29 @@ pub async fn get_remote_scorer_bytes(
Ok(decoded)
}

/// Gets the remote scorer from the server, parses it and returns it as a [`HubPreferentialScorer`]
pub async fn get_remote_scorer(
auth_client: &MutinyAuthClient,
base_url: &str,
network_graph: Arc<NetworkGraph>,
logger: Arc<MutinyLogger>,
) -> Result<HubPreferentialScorer, MutinyError> {
let start = Instant::now();
let scorer_bytes = get_remote_scorer_bytes(auth_client, base_url).await?;
let mut readable_bytes = lightning::io::Cursor::new(scorer_bytes);
let params = decay_params();
let args = (params, network_graph, logger.clone());
let scorer = ProbScorer::read(&mut readable_bytes, args)?;

log_trace!(
logger,
"Retrieved remote scorer in {}ms",
start.elapsed().as_millis()
);

Ok(HubPreferentialScorer::new(scorer))
}

fn write_gossip_data(
storage: &impl MutinyStorage,
last_sync_timestamp: u32,
Expand All @@ -142,9 +169,7 @@ fn write_gossip_data(
}

pub async fn get_gossip_sync(
_storage: &impl MutinyStorage,
remote_scorer_url: Option<String>,
auth_client: Option<Arc<MutinyAuthClient>>,
storage: &impl MutinyStorage,
network: Network,
logger: Arc<MutinyLogger>,
) -> Result<(RapidGossipSync, HubPreferentialScorer), MutinyError> {
Expand All @@ -158,37 +183,28 @@ pub async fn get_gossip_sync(
gossip_data.last_sync_timestamp
);

let start = Instant::now();

// get network graph
let gossip_sync = RapidGossipSync::new(gossip_data.network_graph.clone(), logger.clone());

// Try to get remote scorer if remote_scorer_url and auth_client are available
if let (Some(url), Some(client)) = (remote_scorer_url, &auth_client) {
match get_remote_scorer_bytes(client, &url).await {
Ok(scorer_bytes) => {
let mut readable_bytes = lightning::io::Cursor::new(scorer_bytes);
let params = decay_params();
let args = (
params,
Arc::clone(&gossip_data.network_graph),
Arc::clone(&logger),
);
if let Ok(remote_scorer) = ProbScorer::read(&mut readable_bytes, args) {
log_debug!(logger, "retrieved remote scorer");
let remote_scorer = HubPreferentialScorer::new(remote_scorer);
gossip_data.scorer = Some(remote_scorer);
} else {
log_error!(
logger,
"failed to parse remote scorer, keeping the local one"
);
}
}
Err(_) => {
log_error!(
logger,
"failed to retrieve remote scorer, keeping the local one"
);
}
let scorer_hex: Option<String> = storage.get_data(PROB_SCORER_KEY)?;

if let Some(hex) = scorer_hex {
let scorer_bytes: Vec<u8> = Vec::from_hex(&hex)?;
let mut readable_bytes = lightning::io::Cursor::new(scorer_bytes);
let params = decay_params();
let args = (
params,
Arc::clone(&gossip_data.network_graph),
Arc::clone(&logger),
);
if let Ok(scorer) = ProbScorer::read(&mut readable_bytes, args) {
log_debug!(logger, "retrieved local scorer");
let scorer = HubPreferentialScorer::new(scorer);
gossip_data.scorer = Some(scorer);
} else {
log_error!(logger, "failed to parse local scorer");
}
}

Expand All @@ -201,6 +217,11 @@ pub async fn get_gossip_sync(
}
};

log_trace!(
&logger,
"Gossip sync/Scorer initialized in {}ms",
start.elapsed().as_millis()
);
Ok((gossip_sync, prob_scorer))
}

Expand Down Expand Up @@ -550,7 +571,7 @@ mod test {
let storage = MemoryStorage::default();

let logger = Arc::new(MutinyLogger::default());
let _gossip_sync = get_gossip_sync(&storage, None, None, Network::Regtest, logger.clone())
let _gossip_sync = get_gossip_sync(&storage, Network::Regtest, logger.clone())
.await
.unwrap();

Expand Down
Loading

0 comments on commit 2874b2d

Please sign in to comment.