From 526cadf53924d6487f03cd5c702d8a693ef02cf3 Mon Sep 17 00:00:00 2001 From: asmo Date: Fri, 11 Jul 2025 11:32:11 +0200 Subject: [PATCH] refactor mintd main function (#778) --- crates/cdk-mintd/src/main.rs | 580 +++++++++++++++++++++-------------- 1 file changed, 345 insertions(+), 235 deletions(-) diff --git a/crates/cdk-mintd/src/main.rs b/crates/cdk-mintd/src/main.rs index 0936b69d..36425f1b 100644 --- a/crates/cdk-mintd/src/main.rs +++ b/crates/cdk-mintd/src/main.rs @@ -2,19 +2,24 @@ #![warn(missing_docs)] #![warn(rustdoc::bare_urls)] +// std +#[cfg(feature = "auth")] use std::collections::HashMap; use std::env; use std::net::SocketAddr; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; +// external crates use anyhow::{anyhow, bail, Result}; use axum::Router; use bip39::Mnemonic; -use cdk::cdk_database::{self, MintAuthDatabase}; +// internal crate modules +use cdk::cdk_database::{self, MintDatabase, MintKeysDatabase}; +use cdk::cdk_payment; +use cdk::cdk_payment::MintPayment; use cdk::mint::{MintBuilder, MintMeltLimits}; -// Feature-gated imports #[cfg(any( feature = "cln", feature = "lnbits", @@ -31,17 +36,16 @@ use cdk::nuts::nut19::{CachedEndpoint, Method as NUT19Method, Path as NUT19Path} feature = "fakewallet" ))] use cdk::nuts::CurrencyUnit; -use cdk::nuts::{ - AuthRequired, ContactInfo, MintVersion, PaymentMethod, ProtectedEndpoint, RoutePath, -}; +#[cfg(feature = "auth")] +use cdk::nuts::{AuthRequired, Method, ProtectedEndpoint, RoutePath}; +use cdk::nuts::{ContactInfo, MintVersion, PaymentMethod}; use cdk::types::QuoteTTL; use cdk_axum::cache::HttpCache; -#[cfg(feature = "management-rpc")] -use cdk_mint_rpc::MintRPCServer; use cdk_mintd::cli::CLIArgs; use cdk_mintd::config::{self, DatabaseEngine, LnBackend}; use cdk_mintd::env_vars::ENV_WORK_DIR; use cdk_mintd::setup::LnBackendSetup; +#[cfg(feature = "auth")] use cdk_sqlite::mint::MintSqliteAuthDatabase; use cdk_sqlite::MintSqliteDatabase; use clap::Parser; @@ -68,10 +72,71 @@ compile_error!( "At least one lightning backend feature must be enabled: cln, lnbits, lnd, fakewallet, or grpc-processor" ); +/// The main entry point for the application. +/// +/// This asynchronous function performs the following steps: +/// 1. Executes the initial setup, including loading configurations and initializing the database. +/// 2. Configures a `MintBuilder` instance with the local store and keystore based on the database. +/// 3. Applies additional custom configurations and authentication setup for the `MintBuilder`. +/// 4. Constructs a `Mint` instance from the configured `MintBuilder`. +/// 5. Checks and resolves the status of any pending mint and melt quotes. #[tokio::main] -async fn main() -> anyhow::Result<()> { - let default_filter = "debug"; +async fn main() -> Result<()> { + let (work_dir, settings, localstore, keystore) = initial_setup().await?; + let mint_builder = MintBuilder::new() + .with_localstore(localstore) + .with_keystore(keystore); + + let (mint_builder, ln_routers) = configure_mint_builder(&settings, mint_builder).await?; + #[cfg(feature = "auth")] + let mint_builder = setup_authentication(&settings, &work_dir, mint_builder).await?; + let mint_builder_info = mint_builder.mint_info.clone(); + + let mint = mint_builder.build().await?; + + tracing::debug!("Mint built from builder."); + + let mint = Arc::new(mint); + + // Checks the status of all pending melt quotes + // Pending melt quotes where the payment has gone through inputs are burnt + // Pending melt quotes where the payment has **failed** inputs are reset to unspent + mint.check_pending_melt_quotes().await?; + + start_services( + mint.clone(), + &settings, + ln_routers, + &work_dir, + mint_builder_info, + ) + .await?; + + Ok(()) +} + +/// Performs the initial setup for the application, including configuring tracing, +/// parsing CLI arguments, setting up the working directory, loading settings, +/// and initializing the database connection. +async fn initial_setup() -> Result<( + PathBuf, + config::Settings, + Arc + Send + Sync>, + Arc + Send + Sync>, +)> { + setup_tracing(); + let args = CLIArgs::parse(); + let work_dir = get_work_directory(&args).await?; + + let settings = load_settings(&work_dir, args.config)?; + let (localstore, keystore) = setup_database(&settings, &work_dir).await?; + Ok((work_dir, settings, localstore, keystore)) +} + +/// Sets up and initializes a tracing subscriber with custom log filtering. +fn setup_tracing() { + let default_filter = "debug"; let sqlx_filter = "sqlx=warn"; let hyper_filter = "hyper=warn"; let h2_filter = "h2=warn"; @@ -82,23 +147,27 @@ async fn main() -> anyhow::Result<()> { )); tracing_subscriber::fmt().with_env_filter(env_filter).init(); +} - let args = CLIArgs::parse(); - - let work_dir = if let Some(work_dir) = args.work_dir { +/// Retrieves the work directory based on command-line arguments, environment variables, or system defaults. +async fn get_work_directory(args: &CLIArgs) -> Result { + let work_dir = if let Some(work_dir) = &args.work_dir { tracing::info!("Using work dir from cmd arg"); - work_dir + work_dir.clone() } else if let Ok(env_work_dir) = env::var(ENV_WORK_DIR) { tracing::info!("Using work dir from env var"); env_work_dir.into() } else { work_dir()? }; - tracing::info!("Using work dir: {}", work_dir.display()); + Ok(work_dir) +} +/// Loads the application settings based on a configuration file and environment variables. +fn load_settings(work_dir: &Path, config_path: Option) -> Result { // get config file name from args - let config_file_arg = match args.config { + let config_file_arg = match config_path { Some(c) => c, None => work_dir.join("config.toml"), }; @@ -112,56 +181,126 @@ async fn main() -> anyhow::Result<()> { // This check for any settings defined in ENV VARs // ENV VARS will take **priority** over those in the config - let settings = settings.from_env()?; + settings.from_env() +} - let mut mint_builder = match settings.database.engine { +async fn setup_database( + settings: &config::Settings, + work_dir: &Path, +) -> Result<( + Arc + Send + Sync>, + Arc + Send + Sync>, +)> { + match settings.database.engine { DatabaseEngine::Sqlite => { - let sql_db_path = work_dir.join("cdk-mintd.sqlite"); - #[cfg(not(feature = "sqlcipher"))] - let sqlite_db = MintSqliteDatabase::new(&sql_db_path).await?; #[cfg(feature = "sqlcipher")] - let sqlite_db = MintSqliteDatabase::new(&sql_db_path, args.password.clone()).await?; - - let db = Arc::new(sqlite_db); - MintBuilder::new() - .with_localstore(db.clone()) - .with_keystore(db) + let password = CLIArgs::parse().password; + #[cfg(not(feature = "sqlcipher"))] + let password = String::new(); + let db = setup_sqlite_database(work_dir, Some(password)).await?; + let localstore: Arc + Send + Sync> = db.clone(); + let keystore: Arc + Send + Sync> = db; + Ok((localstore, keystore)) } + } +} + +async fn setup_sqlite_database( + work_dir: &Path, + _password: Option, +) -> Result> { + let sql_db_path = work_dir.join("cdk-mintd.sqlite"); + #[cfg(not(feature = "sqlcipher"))] + let db = MintSqliteDatabase::new(&sql_db_path).await?; + #[cfg(feature = "sqlcipher")] + let db = { + // Get password from command line arguments for sqlcipher + MintSqliteDatabase::new(&sql_db_path, _password.unwrap()).await? }; + Ok(Arc::new(db)) +} - let mut contact_info: Option> = None; +/** + * Configures a `MintBuilder` instance with provided settings and initializes + * routers for Lightning Network backends. + */ +async fn configure_mint_builder( + settings: &config::Settings, + mint_builder: MintBuilder, +) -> Result<(MintBuilder, Vec)> { + let mut ln_routers = vec![]; - if let Some(nostr_contact) = &settings.mint_info.contact_nostr_public_key { - let nostr_contact = ContactInfo::new("nostr".to_string(), nostr_contact.to_string()); + // Configure basic mint information + let mint_builder = configure_basic_info(settings, mint_builder); - contact_info = match contact_info { - Some(mut vec) => { - vec.push(nostr_contact); - Some(vec) - } - None => Some(vec![nostr_contact]), - }; - } - - if let Some(email_contact) = &settings.mint_info.contact_email { - let email_contact = ContactInfo::new("email".to_string(), email_contact.to_string()); - - contact_info = match contact_info { - Some(mut vec) => { - vec.push(email_contact); - Some(vec) - } - None => Some(vec![email_contact]), - }; + // Configure lightning backend + let mint_builder = configure_lightning_backend(settings, mint_builder, &mut ln_routers).await?; + + // Configure signatory or seed + let mint_builder = configure_signing_method(settings, mint_builder).await?; + + // Configure caching + let mint_builder = configure_cache(settings, mint_builder); + + Ok((mint_builder, ln_routers)) +} + +/// Configures basic mint information (name, contact info, descriptions, etc.) +fn configure_basic_info(settings: &config::Settings, mint_builder: MintBuilder) -> MintBuilder { + // Add contact information + let mut contacts = Vec::new(); + if let Some(nostr_key) = &settings.mint_info.contact_nostr_public_key { + contacts.push(ContactInfo::new("nostr".to_string(), nostr_key.to_string())); + } + if let Some(email) = &settings.mint_info.contact_email { + contacts.push(ContactInfo::new("email".to_string(), email.to_string())); } + // Add version information let mint_version = MintVersion::new( "cdk-mintd".to_string(), CARGO_PKG_VERSION.unwrap_or("Unknown").to_string(), ); - let mut ln_routers = vec![]; + // Configure mint builder with basic info + let mut builder = mint_builder + .with_name(settings.mint_info.name.clone()) + .with_version(mint_version) + .with_description(settings.mint_info.description.clone()); + // Add optional information + if let Some(long_description) = &settings.mint_info.description_long { + builder = builder.with_long_description(long_description.to_string()); + } + + for contact in contacts { + builder = builder.add_contact_info(contact); + } + + if let Some(pubkey) = settings.mint_info.pubkey { + builder = builder.with_pubkey(pubkey); + } + + if let Some(icon_url) = &settings.mint_info.icon_url { + builder = builder.with_icon_url(icon_url.to_string()); + } + + if let Some(motd) = &settings.mint_info.motd { + builder = builder.with_motd(motd.to_string()); + } + + if let Some(tos_url) = &settings.mint_info.tos_url { + builder = builder.with_tos_url(tos_url.to_string()); + } + + builder +} +/// Configures Lightning Network backend based on the specified backend type +async fn configure_lightning_backend( + settings: &config::Settings, + mut mint_builder: MintBuilder, + ln_routers: &mut Vec, +) -> Result { let mint_melt_limits = MintMeltLimits { mint_min: settings.ln.min_mint, mint_max: settings.ln.max_mint, @@ -178,74 +317,50 @@ async fn main() -> anyhow::Result<()> { .cln .clone() .expect("Config checked at load that cln is some"); - let cln = cln_settings - .setup(&mut ln_routers, &settings, CurrencyUnit::Msat) - .await?; - let cln = Arc::new(cln); - - mint_builder = mint_builder - .add_ln_backend( - CurrencyUnit::Sat, - PaymentMethod::Bolt11, - mint_melt_limits, - cln.clone(), - ) + .setup(ln_routers, settings, CurrencyUnit::Msat) .await?; - if let Some(input_fee) = settings.info.input_fee_ppk { - mint_builder = mint_builder.set_unit_fee(&CurrencyUnit::Sat, input_fee)?; - } - - let nut17_supported = SupportedMethods::default_bolt11(CurrencyUnit::Sat); - - mint_builder = mint_builder.add_supported_websockets(nut17_supported); + mint_builder = configure_backend_for_unit( + settings, + mint_builder, + CurrencyUnit::Sat, + mint_melt_limits, + Arc::new(cln), + ) + .await?; } #[cfg(feature = "lnbits")] LnBackend::LNbits => { let lnbits_settings = settings.clone().lnbits.expect("Checked on config load"); let lnbits = lnbits_settings - .setup(&mut ln_routers, &settings, CurrencyUnit::Sat) + .setup(ln_routers, settings, CurrencyUnit::Sat) .await?; - mint_builder = mint_builder - .add_ln_backend( - CurrencyUnit::Sat, - PaymentMethod::Bolt11, - mint_melt_limits, - Arc::new(lnbits), - ) - .await?; - if let Some(input_fee) = settings.info.input_fee_ppk { - mint_builder = mint_builder.set_unit_fee(&CurrencyUnit::Sat, input_fee)?; - } - - let nut17_supported = SupportedMethods::default_bolt11(CurrencyUnit::Sat); - - mint_builder = mint_builder.add_supported_websockets(nut17_supported); + mint_builder = configure_backend_for_unit( + settings, + mint_builder, + CurrencyUnit::Sat, + mint_melt_limits, + Arc::new(lnbits), + ) + .await?; } #[cfg(feature = "lnd")] LnBackend::Lnd => { let lnd_settings = settings.clone().lnd.expect("Checked at config load"); let lnd = lnd_settings - .setup(&mut ln_routers, &settings, CurrencyUnit::Msat) + .setup(ln_routers, settings, CurrencyUnit::Msat) .await?; - mint_builder = mint_builder - .add_ln_backend( - CurrencyUnit::Sat, - PaymentMethod::Bolt11, - mint_melt_limits, - Arc::new(lnd), - ) - .await?; - if let Some(input_fee) = settings.info.input_fee_ppk { - mint_builder = mint_builder.set_unit_fee(&CurrencyUnit::Sat, input_fee)?; - } - - let nut17_supported = SupportedMethods::default_bolt11(CurrencyUnit::Sat); - - mint_builder = mint_builder.add_supported_websockets(nut17_supported); + mint_builder = configure_backend_for_unit( + settings, + mint_builder, + CurrencyUnit::Sat, + mint_melt_limits, + Arc::new(lnd), + ) + .await?; } #[cfg(feature = "fakewallet")] LnBackend::FakeWallet => { @@ -254,27 +369,17 @@ async fn main() -> anyhow::Result<()> { for unit in fake_wallet.clone().supported_units { let fake = fake_wallet - .setup(&mut ln_routers, &settings, CurrencyUnit::Sat) - .await - .expect("hhh"); - - let fake = Arc::new(fake); - - mint_builder = mint_builder - .add_ln_backend( - unit.clone(), - PaymentMethod::Bolt11, - mint_melt_limits, - fake.clone(), - ) + .setup(ln_routers, settings, CurrencyUnit::Sat) .await?; - if let Some(input_fee) = settings.info.input_fee_ppk { - mint_builder = mint_builder.set_unit_fee(&unit, input_fee)?; - } - let nut17_supported = SupportedMethods::default_bolt11(unit); - - mint_builder = mint_builder.add_supported_websockets(nut17_supported); + mint_builder = configure_backend_for_unit( + settings, + mint_builder, + unit.clone(), + mint_melt_limits, + Arc::new(fake), + ) + .await?; } } #[cfg(feature = "grpc-processor")] @@ -290,123 +395,132 @@ async fn main() -> anyhow::Result<()> { grpc_processor.port ); - tracing::info!("{:?}", grpc_processor); - for unit in grpc_processor.clone().supported_units { tracing::debug!("Adding unit: {:?}", unit); - let processor = grpc_processor - .setup(&mut ln_routers, &settings, unit.clone()) + .setup(ln_routers, settings, unit.clone()) .await?; - mint_builder = mint_builder - .add_ln_backend( - unit.clone(), - PaymentMethod::Bolt11, - mint_melt_limits, - Arc::new(processor), - ) - .await?; - if let Some(input_fee) = settings.info.input_fee_ppk { - mint_builder = mint_builder.set_unit_fee(&unit, input_fee)?; - } - - let nut17_supported = SupportedMethods::default_bolt11(unit); - mint_builder = mint_builder.add_supported_websockets(nut17_supported); + mint_builder = configure_backend_for_unit( + settings, + mint_builder, + unit.clone(), + mint_melt_limits, + Arc::new(processor), + ) + .await?; } } LnBackend::None => { tracing::error!( - "Pyament backend was not set or feature disabled. {:?}", + "Payment backend was not set or feature disabled. {:?}", settings.ln.ln_backend ); - bail!("Ln backend must be") + bail!("Lightning backend must be configured"); } }; - if let Some(long_description) = &settings.mint_info.description_long { - mint_builder = mint_builder.with_long_description(long_description.to_string()); - } - - if let Some(contact_info) = contact_info { - for info in contact_info { - mint_builder = mint_builder.add_contact_info(info); - } - } - - if let Some(pubkey) = settings.mint_info.pubkey { - mint_builder = mint_builder.with_pubkey(pubkey); - } - - if let Some(icon_url) = &settings.mint_info.icon_url { - mint_builder = mint_builder.with_icon_url(icon_url.to_string()); - } - - if let Some(motd) = settings.mint_info.motd { - mint_builder = mint_builder.with_motd(motd); - } - - if let Some(tos_url) = &settings.mint_info.tos_url { - mint_builder = mint_builder.with_tos_url(tos_url.to_string()); - } + Ok(mint_builder) +} +/// Helper function to configure a mint builder with a lightning backend for a specific currency unit +async fn configure_backend_for_unit( + settings: &config::Settings, + mut mint_builder: MintBuilder, + unit: cdk::nuts::CurrencyUnit, + mint_melt_limits: MintMeltLimits, + backend: Arc + Send + Sync>, +) -> Result { mint_builder = mint_builder - .with_name(settings.mint_info.name) - .with_version(mint_version.clone()) - .with_description(settings.mint_info.description); + .add_ln_backend( + unit.clone(), + PaymentMethod::Bolt11, + mint_melt_limits, + backend, + ) + .await?; - mint_builder = if let Some(signatory_url) = settings.info.signatory_url { + if let Some(input_fee) = settings.info.input_fee_ppk { + mint_builder = mint_builder.set_unit_fee(&unit, input_fee)?; + } + + let nut17_supported = SupportedMethods::default_bolt11(unit); + mint_builder = mint_builder.add_supported_websockets(nut17_supported); + + Ok(mint_builder) +} + +/// Configures the signing method (remote signatory or local seed) +async fn configure_signing_method( + settings: &config::Settings, + mint_builder: MintBuilder, +) -> Result { + if let Some(signatory_url) = settings.info.signatory_url.clone() { tracing::info!( "Connecting to remote signatory to {} with certs {:?}", signatory_url, - settings.info.signatory_certs + settings.info.signatory_certs.clone() ); - mint_builder.with_signatory(Arc::new( - cdk_signatory::SignatoryRpcClient::new(signatory_url, settings.info.signatory_certs) - .await?, - )) + + Ok(mint_builder.with_signatory(Arc::new( + cdk_signatory::SignatoryRpcClient::new( + signatory_url, + settings.info.signatory_certs.clone(), + ) + .await?, + ))) } else if let Some(mnemonic) = settings .info .mnemonic + .clone() .map(|s| Mnemonic::from_str(&s)) .transpose()? { - mint_builder.with_seed(mnemonic.to_seed_normalized("").to_vec()) + Ok(mint_builder.with_seed(mnemonic.to_seed_normalized("").to_vec())) } else { bail!("No seed nor remote signatory set"); - }; + } +} +/// Configures cache settings +fn configure_cache(settings: &config::Settings, mint_builder: MintBuilder) -> MintBuilder { let cached_endpoints = vec![ CachedEndpoint::new(NUT19Method::Post, NUT19Path::MintBolt11), CachedEndpoint::new(NUT19Method::Post, NUT19Path::MeltBolt11), CachedEndpoint::new(NUT19Method::Post, NUT19Path::Swap), ]; - let cache: HttpCache = settings.info.http_cache.into(); + let cache: HttpCache = settings.info.http_cache.clone().into(); + mint_builder.add_cache(Some(cache.ttl.as_secs()), cached_endpoints) +} - mint_builder = mint_builder.add_cache(Some(cache.ttl.as_secs()), cached_endpoints); - - // Add auth to mint - if let Some(auth_settings) = settings.auth { +#[cfg(feature = "auth")] +async fn setup_authentication( + settings: &config::Settings, + work_dir: &Path, + mut mint_builder: MintBuilder, +) -> Result { + if let Some(auth_settings) = settings.auth.clone() { tracing::info!("Auth settings are defined. {:?}", auth_settings); - let auth_localstore: Arc + Send + Sync> = - match settings.database.engine { - DatabaseEngine::Sqlite => { - let sql_db_path = work_dir.join("cdk-mintd-auth.sqlite"); - #[cfg(not(feature = "sqlcipher"))] - let sqlite_db = MintSqliteAuthDatabase::new(&sql_db_path).await?; - #[cfg(feature = "sqlcipher")] - let sqlite_db = - MintSqliteAuthDatabase::new(&sql_db_path, args.password).await?; - - Arc::new(sqlite_db) - } - }; + let auth_localstore: Arc< + dyn cdk_database::MintAuthDatabase + Send + Sync, + > = match settings.database.engine { + DatabaseEngine::Sqlite => { + let sql_db_path = work_dir.join("cdk-mintd-auth.sqlite"); + #[cfg(feature = "sqlcipher")] + let password = CLIArgs::parse().password; + #[cfg(feature = "sqlcipher")] + let sqlite_db = MintSqliteAuthDatabase::new(&sql_db_path, password).await?; + #[cfg(not(feature = "sqlcipher"))] + let sqlite_db = MintSqliteAuthDatabase::new(&sql_db_path).await?; + Arc::new(sqlite_db) + } + }; mint_builder = mint_builder.with_auth_localstore(auth_localstore.clone()); let mint_blind_auth_endpoint = - ProtectedEndpoint::new(cdk::nuts::Method::Post, RoutePath::MintBlindAuth); + ProtectedEndpoint::new(Method::Post, RoutePath::MintBlindAuth); mint_builder = mint_builder.set_clear_auth_settings( auth_settings.openid_discovery, @@ -421,12 +535,10 @@ async fn main() -> anyhow::Result<()> { let mut unprotected_endpoints = vec![]; { - let mint_quote_protected_endpoint = ProtectedEndpoint::new( - cdk::nuts::Method::Post, - cdk::nuts::RoutePath::MintQuoteBolt11, - ); + let mint_quote_protected_endpoint = + ProtectedEndpoint::new(Method::Post, RoutePath::MintQuoteBolt11); let mint_protected_endpoint = - ProtectedEndpoint::new(cdk::nuts::Method::Post, cdk::nuts::RoutePath::MintBolt11); + ProtectedEndpoint::new(Method::Post, RoutePath::MintBolt11); if auth_settings.enabled_mint { protected_endpoints.insert(mint_quote_protected_endpoint, AuthRequired::Blind); @@ -441,12 +553,10 @@ async fn main() -> anyhow::Result<()> { } { - let melt_quote_protected_endpoint = ProtectedEndpoint::new( - cdk::nuts::Method::Post, - cdk::nuts::RoutePath::MeltQuoteBolt11, - ); + let melt_quote_protected_endpoint = + ProtectedEndpoint::new(Method::Post, RoutePath::MeltQuoteBolt11); let melt_protected_endpoint = - ProtectedEndpoint::new(cdk::nuts::Method::Post, cdk::nuts::RoutePath::MeltBolt11); + ProtectedEndpoint::new(Method::Post, RoutePath::MeltBolt11); if auth_settings.enabled_melt { protected_endpoints.insert(melt_quote_protected_endpoint, AuthRequired::Blind); @@ -461,8 +571,7 @@ async fn main() -> anyhow::Result<()> { } { - let swap_protected_endpoint = - ProtectedEndpoint::new(cdk::nuts::Method::Post, cdk::nuts::RoutePath::Swap); + let swap_protected_endpoint = ProtectedEndpoint::new(Method::Post, RoutePath::Swap); if auth_settings.enabled_swap { protected_endpoints.insert(swap_protected_endpoint, AuthRequired::Blind); @@ -473,10 +582,8 @@ async fn main() -> anyhow::Result<()> { } { - let check_mint_protected_endpoint = ProtectedEndpoint::new( - cdk::nuts::Method::Get, - cdk::nuts::RoutePath::MintQuoteBolt11, - ); + let check_mint_protected_endpoint = + ProtectedEndpoint::new(Method::Get, RoutePath::MintQuoteBolt11); if auth_settings.enabled_check_mint_quote { protected_endpoints.insert(check_mint_protected_endpoint, AuthRequired::Blind); @@ -487,10 +594,8 @@ async fn main() -> anyhow::Result<()> { } { - let check_melt_protected_endpoint = ProtectedEndpoint::new( - cdk::nuts::Method::Get, - cdk::nuts::RoutePath::MeltQuoteBolt11, - ); + let check_melt_protected_endpoint = + ProtectedEndpoint::new(Method::Get, RoutePath::MeltQuoteBolt11); if auth_settings.enabled_check_melt_quote { protected_endpoints.insert(check_melt_protected_endpoint, AuthRequired::Blind); @@ -502,7 +607,7 @@ async fn main() -> anyhow::Result<()> { { let restore_protected_endpoint = - ProtectedEndpoint::new(cdk::nuts::Method::Post, cdk::nuts::RoutePath::Restore); + ProtectedEndpoint::new(Method::Post, RoutePath::Restore); if auth_settings.enabled_restore { protected_endpoints.insert(restore_protected_endpoint, AuthRequired::Blind); @@ -514,7 +619,7 @@ async fn main() -> anyhow::Result<()> { { let state_protected_endpoint = - ProtectedEndpoint::new(cdk::nuts::Method::Post, cdk::nuts::RoutePath::Checkstate); + ProtectedEndpoint::new(Method::Post, RoutePath::Checkstate); if auth_settings.enabled_check_proof_state { protected_endpoints.insert(state_protected_endpoint, AuthRequired::Blind); @@ -532,20 +637,19 @@ async fn main() -> anyhow::Result<()> { tx.add_protected_endpoints(protected_endpoints).await?; tx.commit().await?; } + Ok(mint_builder) +} - let mint = mint_builder.build().await?; - - tracing::debug!("Mint built from builder."); - - let mint = Arc::new(mint); - - // Checks the status of all pending melt quotes - // Pending melt quotes where the payment has gone through inputs are burnt - // Pending melt quotes where the payment has **failed** inputs are reset to unspent - mint.check_pending_melt_quotes().await?; - - let listen_addr = settings.info.listen_host; +async fn start_services( + mint: Arc, + settings: &config::Settings, + ln_routers: Vec, + _work_dir: &Path, + mint_builder_info: cdk::nuts::MintInfo, +) -> Result<()> { + let listen_addr = settings.info.listen_host.clone(); let listen_port = settings.info.listen_port; + let cache: HttpCache = settings.info.http_cache.clone().into(); let v1_service = cdk_axum::create_mint_router_with_custom_cache(Arc::clone(&mint), cache).await?; @@ -590,13 +694,13 @@ async fn main() -> anyhow::Result<()> { #[cfg(feature = "management-rpc")] { - if let Some(rpc_settings) = settings.mint_management_rpc { + if let Some(rpc_settings) = settings.mint_management_rpc.clone() { if rpc_settings.enabled { let addr = rpc_settings.address.unwrap_or("127.0.0.1".to_string()); let port = rpc_settings.port.unwrap_or(8086); - let mut mint_rpc = MintRPCServer::new(&addr, port, mint.clone())?; + let mut mint_rpc = cdk_mint_rpc::MintRPCServer::new(&addr, port, mint.clone())?; - let tls_dir = rpc_settings.tls_dir_path.unwrap_or(work_dir.join("tls")); + let tls_dir = rpc_settings.tls_dir_path.unwrap_or(_work_dir.join("tls")); if !tls_dir.exists() { tracing::error!("TLS directory does not exist: {}", tls_dir.display()); @@ -615,13 +719,17 @@ async fn main() -> anyhow::Result<()> { if rpc_enabled { if mint.mint_info().await.is_err() { tracing::info!("Mint info not set on mint, setting."); - mint.set_mint_info(mint_builder.mint_info).await?; + mint.set_mint_info(mint_builder_info).await?; mint.set_quote_ttl(QuoteTTL::new(10_000, 10_000)).await?; } else { if mint.localstore.get_quote_ttl().await.is_err() { mint.set_quote_ttl(QuoteTTL::new(10_000, 10_000)).await?; } - + // Add version information + let mint_version = MintVersion::new( + "cdk-mintd".to_string(), + CARGO_PKG_VERSION.unwrap_or("Unknown").to_string(), + ); let mut stored_mint_info = mint.mint_info().await?; stored_mint_info.version = Some(mint_version); mint.set_mint_info(stored_mint_info).await?; @@ -630,7 +738,7 @@ async fn main() -> anyhow::Result<()> { } } else { tracing::warn!("RPC not enabled, using mint info from config."); - mint.set_mint_info(mint_builder.mint_info).await?; + mint.set_mint_info(mint_builder_info).await?; mint.set_quote_ttl(QuoteTTL::new(10_000, 10_000)).await?; } @@ -640,6 +748,7 @@ async fn main() -> anyhow::Result<()> { tracing::debug!("listening on {}", listener.local_addr().unwrap()); + // Wait for axum server to complete let axum_result = axum::serve(listener, mint_service).with_graceful_shutdown(shutdown_signal()); match axum_result.await { @@ -653,6 +762,7 @@ async fn main() -> anyhow::Result<()> { } } + // Notify all waiting tasks to shutdown shutdown.notify_waiters(); #[cfg(feature = "management-rpc")]