瀏覽代碼

Merge branch 'main' into get-active-mint-quotes

David Caseria 3 周之前
父節點
當前提交
2d08e6c080
共有 1 個文件被更改,包括 340 次插入230 次删除
  1. 340 230
      crates/cdk-mintd/src/main.rs

+ 340 - 230
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<dyn MintDatabase<cdk_database::Error> + Send + Sync>,
+    Arc<dyn MintKeysDatabase<Err = cdk_database::Error> + 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<PathBuf> {
+    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<PathBuf>) -> Result<config::Settings> {
     // 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<dyn MintDatabase<cdk_database::Error> + Send + Sync>,
+    Arc<dyn MintKeysDatabase<Err = cdk_database::Error> + 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<dyn MintDatabase<cdk_database::Error> + Send + Sync> = db.clone();
+            let keystore: Arc<dyn MintKeysDatabase<Err = cdk_database::Error> + Send + Sync> = db;
+            Ok((localstore, keystore))
         }
+    }
+}
+
+async fn setup_sqlite_database(
+    work_dir: &Path,
+    _password: Option<String>,
+) -> Result<Arc<MintSqliteDatabase>> {
+    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<Vec<ContactInfo>> = 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<Router>)> {
+    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]),
-        };
-    }
+    // Configure lightning backend
+    let mint_builder = configure_lightning_backend(settings, mint_builder, &mut ln_routers).await?;
 
-    if let Some(email_contact) = &settings.mint_info.contact_email {
-        let email_contact = ContactInfo::new("email".to_string(), email_contact.to_string());
+    // Configure signatory or seed
+    let mint_builder = configure_signing_method(settings, mint_builder).await?;
 
-        contact_info = match contact_info {
-            Some(mut vec) => {
-                vec.push(email_contact);
-                Some(vec)
-            }
-            None => Some(vec![email_contact]),
-        };
+    // 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<Router>,
+) -> Result<MintBuilder> {
     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)
-                .await?;
-
-            mint_builder = mint_builder
-                .add_ln_backend(
-                    CurrencyUnit::Sat,
-                    PaymentMethod::Bolt11,
-                    mint_melt_limits,
-                    Arc::new(lnd),
-                )
+                .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(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);
-    }
+    Ok(mint_builder)
+}
 
-    if let Some(icon_url) = &settings.mint_info.icon_url {
-        mint_builder = mint_builder.with_icon_url(icon_url.to_string());
-    }
+/// 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<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>,
+) -> Result<MintBuilder> {
+    mint_builder = mint_builder
+        .add_ln_backend(
+            unit.clone(),
+            PaymentMethod::Bolt11,
+            mint_melt_limits,
+            backend,
+        )
+        .await?;
 
-    if let Some(motd) = settings.mint_info.motd {
-        mint_builder = mint_builder.with_motd(motd);
+    if let Some(input_fee) = settings.info.input_fee_ppk {
+        mint_builder = mint_builder.set_unit_fee(&unit, input_fee)?;
     }
 
-    if let Some(tos_url) = &settings.mint_info.tos_url {
-        mint_builder = mint_builder.with_tos_url(tos_url.to_string());
-    }
+    let nut17_supported = SupportedMethods::default_bolt11(unit);
+    mint_builder = mint_builder.add_supported_websockets(nut17_supported);
 
-    mint_builder = mint_builder
-        .with_name(settings.mint_info.name)
-        .with_version(mint_version.clone())
-        .with_description(settings.mint_info.description);
+    Ok(mint_builder)
+}
 
-    mint_builder = if let Some(signatory_url) = settings.info.signatory_url {
+/// Configures the signing method (remote signatory or local seed)
+async fn configure_signing_method(
+    settings: &config::Settings,
+    mint_builder: MintBuilder,
+) -> Result<MintBuilder> {
+    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();
-
-    mint_builder = mint_builder.add_cache(Some(cache.ttl.as_secs()), cached_endpoints);
+    let cache: HttpCache = settings.info.http_cache.clone().into();
+    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<MintBuilder> {
+    if let Some(auth_settings) = settings.auth.clone() {
         tracing::info!("Auth settings are defined. {:?}", auth_settings);
-        let auth_localstore: Arc<dyn MintAuthDatabase<Err = cdk_database::Error> + 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<Err = cdk_database::Error> + 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<cdk::mint::Mint>,
+    settings: &config::Settings,
+    ln_routers: Vec<Router>,
+    _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")]