From b787951dbc09cf7451660db428f9074983740e19 Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sun, 9 Mar 2025 14:18:19 +0000 Subject: [PATCH] feat: Add feature gates for CLN, LND, fakewallet and LNbits backends (#638) * feat: Add feature gates for CLN, LND, fakewallet and LNbits backends --- .github/workflows/ci.yml | 18 +- CHANGELOG.md | 2 + crates/cdk-mintd/Cargo.toml | 15 +- crates/cdk-mintd/src/config.rs | 27 +- crates/cdk-mintd/src/env_vars.rs | 416 ------------------ crates/cdk-mintd/src/env_vars/cln.rs | 44 ++ crates/cdk-mintd/src/env_vars/common.rs | 13 + crates/cdk-mintd/src/env_vars/fake_wallet.rs | 55 +++ crates/cdk-mintd/src/env_vars/info.rs | 57 +++ crates/cdk-mintd/src/env_vars/ln.rs | 56 +++ crates/cdk-mintd/src/env_vars/lnbits.rs | 42 ++ crates/cdk-mintd/src/env_vars/lnd.rs | 43 ++ .../cdk-mintd/src/env_vars/management_rpc.rs | 37 ++ crates/cdk-mintd/src/env_vars/mint_info.rs | 63 +++ crates/cdk-mintd/src/env_vars/mod.rs | 87 ++++ crates/cdk-mintd/src/lib.rs | 2 + crates/cdk-mintd/src/main.rs | 22 + crates/cdk-mintd/src/setup.rs | 15 +- 18 files changed, 586 insertions(+), 428 deletions(-) delete mode 100644 crates/cdk-mintd/src/env_vars.rs create mode 100644 crates/cdk-mintd/src/env_vars/cln.rs create mode 100644 crates/cdk-mintd/src/env_vars/common.rs create mode 100644 crates/cdk-mintd/src/env_vars/fake_wallet.rs create mode 100644 crates/cdk-mintd/src/env_vars/info.rs create mode 100644 crates/cdk-mintd/src/env_vars/ln.rs create mode 100644 crates/cdk-mintd/src/env_vars/lnbits.rs create mode 100644 crates/cdk-mintd/src/env_vars/lnd.rs create mode 100644 crates/cdk-mintd/src/env_vars/management_rpc.rs create mode 100644 crates/cdk-mintd/src/env_vars/mint_info.rs create mode 100644 crates/cdk-mintd/src/env_vars/mod.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ca7ef06..73910c13 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,11 +105,19 @@ jobs: -p cdk-fake-wallet, --bin cdk-cli, --bin cdk-mintd, - --bin cdk-mintd --no-default-features --features swagger, - --bin cdk-mintd --no-default-features --features redis, - --bin cdk-mintd --no-default-features --features "redis swagger", - --bin cdk-mintd --no-default-features --features management-rpc, - --bin cdk-mintd --no-default-features --features redb, + --bin cdk-mintd --features redis, + --bin cdk-mintd --features redb, + --bin cdk-mintd --features "redis swagger redb", + --bin cdk-mintd --no-default-features --features lnd, + --bin cdk-mintd --no-default-features --features cln, + --bin cdk-mintd --no-default-features --features lnbits, + --bin cdk-mintd --no-default-features --features fakewallet, + --bin cdk-mintd --no-default-features --features "management-rpc lnd", + --bin cdk-mintd --no-default-features --features "management-rpc cln", + --bin cdk-mintd --no-default-features --features "management-rpc lnbits", + --bin cdk-mintd --no-default-features --features "swagger lnd", + --bin cdk-mintd --no-default-features --features "swagger cln", + --bin cdk-mintd --no-default-features --features "swagger lnbits", --bin cdk-mint-cli, ] steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 16765789..7f45c37d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Updated MSRV to 1.75.0 ([thesimplekid]). - cdk-sqlite: Do not use `UPDATE OR REPLACE` ([crodas]). - cdk: Refactor keyset init ([lollerfirst]). +- Feature-gated lightning backends (CLN, LND, LNbits, FakeWallet) for selective compilation ([thesimplekid]). ### Added - Added redb feature to mintd in order to meet MSRV target ([thesimplekid]). - cdk-sqlite: In memory sqlite database ([crodas]). @@ -17,6 +18,7 @@ - cdk: Add tos_url setter to `MintBuilder` ([thesimplekid]). - Added optional "request" and "unit" fields to MeltQuoteBolt11Response [NUT Change](https://github.com/cashubtc/nuts/pull/235) ([thesimplekid]). - Added optional "amount" and "unit" fields to MintQuoteBolt11Response [NUT Change](https://github.com/cashubtc/nuts/pull/235) ([thesimplekid]). +- Compile-time error when no lightning backend features are enabled ([thesimplekid]). ### Removed - Remove support for Memory Database in cdk ([crodas]). - Remove `AmountStr` ([crodas]). diff --git a/crates/cdk-mintd/Cargo.toml b/crates/cdk-mintd/Cargo.toml index 66e463fc..b4bd2d5e 100644 --- a/crates/cdk-mintd/Cargo.toml +++ b/crates/cdk-mintd/Cargo.toml @@ -10,12 +10,17 @@ description = "CDK mint binary" rust-version = "1.75.0" [features] -default = ["management-rpc"] +default = ["management-rpc", "cln", "lnd", "lnbits", "fakewallet"] +# Ensure at least one lightning backend is enabled swagger = ["cdk-axum/swagger", "dep:utoipa", "dep:utoipa-swagger-ui"] redis = ["cdk-axum/redis"] management-rpc = ["cdk-mint-rpc"] # MSRV is not commited to with redb enabled redb = ["dep:cdk-redb"] +cln = ["dep:cdk-cln"] +lnd = ["dep:cdk-lnd"] +lnbits = ["dep:cdk-lnbits"] +fakewallet = ["dep:cdk-fake-wallet"] [dependencies] anyhow.workspace = true @@ -30,10 +35,10 @@ cdk-redb = { workspace = true, features = [ cdk-sqlite = { workspace = true, features = [ "mint", ] } -cdk-cln.workspace = true -cdk-lnbits.workspace = true -cdk-lnd.workspace = true -cdk-fake-wallet.workspace = true +cdk-cln = { workspace = true, optional = true } +cdk-lnbits = { workspace = true, optional = true } +cdk-lnd = { workspace = true, optional = true } +cdk-fake-wallet = { workspace = true, optional = true } cdk-axum.workspace = true cdk-mint-rpc = { workspace = true, optional = true } config = { version = "0.13.3", features = ["toml"] } diff --git a/crates/cdk-mintd/src/config.rs b/crates/cdk-mintd/src/config.rs index 6c7ec97e..5c6c10d0 100644 --- a/crates/cdk-mintd/src/config.rs +++ b/crates/cdk-mintd/src/config.rs @@ -1,7 +1,9 @@ use std::path::PathBuf; use bitcoin::hashes::{sha256, Hash}; -use cdk::nuts::{CurrencyUnit, PublicKey}; +#[cfg(feature = "fakewallet")] +use cdk::nuts::CurrencyUnit; +use cdk::nuts::PublicKey; use cdk::Amount; use cdk_axum::cache; use config::{Config, ConfigError, File}; @@ -46,9 +48,13 @@ impl std::fmt::Debug for Info { pub enum LnBackend { #[default] None, + #[cfg(feature = "cln")] Cln, + #[cfg(feature = "lnbits")] LNbits, + #[cfg(feature = "fakewallet")] FakeWallet, + #[cfg(feature = "lnd")] Lnd, } @@ -57,9 +63,13 @@ impl std::str::FromStr for LnBackend { fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { + #[cfg(feature = "cln")] "cln" => Ok(LnBackend::Cln), + #[cfg(feature = "lnbits")] "lnbits" => Ok(LnBackend::LNbits), + #[cfg(feature = "fakewallet")] "fakewallet" => Ok(LnBackend::FakeWallet), + #[cfg(feature = "lnd")] "lnd" => Ok(LnBackend::Lnd), _ => Err(format!("Unknown Lightning backend: {}", s)), } @@ -89,6 +99,7 @@ impl Default for Ln { } } +#[cfg(feature = "lnbits")] #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct LNbits { pub admin_api_key: String, @@ -98,6 +109,7 @@ pub struct LNbits { pub reserve_fee_min: Amount, } +#[cfg(feature = "cln")] #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Cln { pub rpc_path: PathBuf, @@ -107,6 +119,7 @@ pub struct Cln { pub reserve_fee_min: Amount, } +#[cfg(feature = "lnd")] #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Lnd { pub address: String, @@ -116,6 +129,7 @@ pub struct Lnd { pub reserve_fee_min: Amount, } +#[cfg(feature = "fakewallet")] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FakeWallet { pub supported_units: Vec, @@ -127,6 +141,7 @@ pub struct FakeWallet { pub max_delay_time: u64, } +#[cfg(feature = "fakewallet")] impl Default for FakeWallet { fn default() -> Self { Self { @@ -140,10 +155,12 @@ impl Default for FakeWallet { } // Helper functions to provide default values +#[cfg(feature = "fakewallet")] fn default_min_delay_time() -> u64 { 1 } +#[cfg(feature = "fakewallet")] fn default_max_delay_time() -> u64 { 3 } @@ -181,9 +198,13 @@ pub struct Settings { pub info: Info, pub mint_info: MintInfo, pub ln: Ln, + #[cfg(feature = "cln")] pub cln: Option, + #[cfg(feature = "lnbits")] pub lnbits: Option, + #[cfg(feature = "lnd")] pub lnd: Option, + #[cfg(feature = "fakewallet")] pub fake_wallet: Option, pub database: Database, #[cfg(feature = "management-rpc")] @@ -270,20 +291,24 @@ impl Settings { match settings.ln.ln_backend { LnBackend::None => panic!("Ln backend must be set"), + #[cfg(feature = "cln")] LnBackend::Cln => assert!( settings.cln.is_some(), "CLN backend requires a valid config." ), + #[cfg(feature = "lnbits")] LnBackend::LNbits => assert!( settings.lnbits.is_some(), "LNbits backend requires a valid config" ), + #[cfg(feature = "lnd")] LnBackend::Lnd => { assert!( settings.lnd.is_some(), "LND backend requires a valid config." ) } + #[cfg(feature = "fakewallet")] LnBackend::FakeWallet => assert!( settings.fake_wallet.is_some(), "FakeWallet backend requires a valid config." diff --git a/crates/cdk-mintd/src/env_vars.rs b/crates/cdk-mintd/src/env_vars.rs deleted file mode 100644 index 306a347f..00000000 --- a/crates/cdk-mintd/src/env_vars.rs +++ /dev/null @@ -1,416 +0,0 @@ -use std::env; -use std::path::PathBuf; -use std::str::FromStr; - -use anyhow::{anyhow, bail, Result}; -use cdk::nuts::CurrencyUnit; - -#[cfg(feature = "management-rpc")] -use crate::config::MintManagementRpc; -use crate::config::{ - Cln, Database, DatabaseEngine, FakeWallet, Info, LNbits, Ln, LnBackend, Lnd, MintInfo, Settings, -}; - -pub const ENV_WORK_DIR: &str = "CDK_MINTD_WORK_DIR"; - -pub const DATABASE_ENV_VAR: &str = "CDK_MINTD_DATABASE"; -pub const ENV_URL: &str = "CDK_MINTD_URL"; -pub const ENV_LISTEN_HOST: &str = "CDK_MINTD_LISTEN_HOST"; -pub const ENV_LISTEN_PORT: &str = "CDK_MINTD_LISTEN_PORT"; -pub const ENV_MNEMONIC: &str = "CDK_MINTD_MNEMONIC"; -pub const ENV_SECONDS_QUOTE_VALID: &str = "CDK_MINTD_SECONDS_QUOTE_VALID"; -pub const ENV_CACHE_SECONDS: &str = "CDK_MINTD_CACHE_SECONDS"; -pub const ENV_EXTEND_CACHE_SECONDS: &str = "CDK_MINTD_EXTEND_CACHE_SECONDS"; -pub const ENV_INPUT_FEE_PPK: &str = "CDK_MINTD_INPUT_FEE_PPK"; -pub const ENV_ENABLE_SWAGGER: &str = "CDK_MINTD_ENABLE_SWAGGER"; -// MintInfo -pub const ENV_MINT_NAME: &str = "CDK_MINTD_MINT_NAME"; -pub const ENV_MINT_PUBKEY: &str = "CDK_MINTD_MINT_PUBKEY"; -pub const ENV_MINT_DESCRIPTION: &str = "CDK_MINTD_MINT_DESCRIPTION"; -pub const ENV_MINT_DESCRIPTION_LONG: &str = "CDK_MINTD_MINT_DESCRIPTION_LONG"; -pub const ENV_MINT_ICON_URL: &str = "CDK_MINTD_MINT_ICON_URL"; -pub const ENV_MINT_MOTD: &str = "CDK_MINTD_MINT_MOTD"; -pub const ENV_MINT_CONTACT_NOSTR: &str = "CDK_MINTD_MINT_CONTACT_NOSTR"; -pub const ENV_MINT_CONTACT_EMAIL: &str = "CDK_MINTD_MINT_CONTACT_EMAIL"; -pub const ENV_MINT_TOS_URL: &str = "CDK_MINTD_MINT_TOS_URL"; -// LN -pub const ENV_LN_BACKEND: &str = "CDK_MINTD_LN_BACKEND"; -pub const ENV_LN_INVOICE_DESCRIPTION: &str = "CDK_MINTD_LN_INVOICE_DESCRIPTION"; -pub const ENV_LN_MIN_MINT: &str = "CDK_MINTD_LN_MIN_MINT"; -pub const ENV_LN_MAX_MINT: &str = "CDK_MINTD_LN_MAX_MINT"; -pub const ENV_LN_MIN_MELT: &str = "CDK_MINTD_LN_MIN_MELT"; -pub const ENV_LN_MAX_MELT: &str = "CDK_MINTD_LN_MAX_MELT"; -// CLN -pub const ENV_CLN_RPC_PATH: &str = "CDK_MINTD_CLN_RPC_PATH"; -pub const ENV_CLN_BOLT12: &str = "CDK_MINTD_CLN_BOLT12"; -pub const ENV_CLN_FEE_PERCENT: &str = "CDK_MINTD_CLN_FEE_PERCENT"; -pub const ENV_CLN_RESERVE_FEE_MIN: &str = "CDK_MINTD_CLN_RESERVE_FEE_MIN"; -// LND environment variables -pub const ENV_LND_ADDRESS: &str = "CDK_MINTD_LND_ADDRESS"; -pub const ENV_LND_CERT_FILE: &str = "CDK_MINTD_LND_CERT_FILE"; -pub const ENV_LND_MACAROON_FILE: &str = "CDK_MINTD_LND_MACAROON_FILE"; -pub const ENV_LND_FEE_PERCENT: &str = "CDK_MINTD_LND_FEE_PERCENT"; -pub const ENV_LND_RESERVE_FEE_MIN: &str = "CDK_MINTD_LND_RESERVE_FEE_MIN"; -// LNBits -pub const ENV_LNBITS_ADMIN_API_KEY: &str = "CDK_MINTD_LNBITS_ADMIN_API_KEY"; -pub const ENV_LNBITS_INVOICE_API_KEY: &str = "CDK_MINTD_LNBITS_INVOICE_API_KEY"; -pub const ENV_LNBITS_API: &str = "CDK_MINTD_LNBITS_API"; -pub const ENV_LNBITS_FEE_PERCENT: &str = "CDK_MINTD_LNBITS_FEE_PERCENT"; -pub const ENV_LNBITS_RESERVE_FEE_MIN: &str = "CDK_MINTD_LNBITS_RESERVE_FEE_MIN"; -// Fake Wallet -pub const ENV_FAKE_WALLET_SUPPORTED_UNITS: &str = "CDK_MINTD_FAKE_WALLET_SUPPORTED_UNITS"; -pub const ENV_FAKE_WALLET_FEE_PERCENT: &str = "CDK_MINTD_FAKE_WALLET_FEE_PERCENT"; -pub const ENV_FAKE_WALLET_RESERVE_FEE_MIN: &str = "CDK_MINTD_FAKE_WALLET_RESERVE_FEE_MIN"; -pub const ENV_FAKE_WALLET_MIN_DELAY: &str = "CDK_MINTD_FAKE_WALLET_MIN_DELAY"; -pub const ENV_FAKE_WALLET_MAX_DELAY: &str = "CDK_MINTD_FAKE_WALLET_MAX_DELAY"; -// Mint RPC Server -#[cfg(feature = "management-rpc")] -pub const ENV_MINT_MANAGEMENT_ENABLED: &str = "CDK_MINTD_MINT_MANAGEMENT_ENABLED"; -#[cfg(feature = "management-rpc")] -pub const ENV_MINT_MANAGEMENT_ADDRESS: &str = "CDK_MINTD_MANAGEMENT_ADDRESS"; -#[cfg(feature = "management-rpc")] -pub const ENV_MINT_MANAGEMENT_PORT: &str = "CDK_MINTD_MANAGEMENT_PORT"; -#[cfg(feature = "management-rpc")] -pub const ENV_MINT_MANAGEMENT_TLS_DIR_PATH: &str = "CDK_MINTD_MANAGEMENT_TLS_DIR_PATH"; - -impl Settings { - pub fn from_env(&mut self) -> Result { - if let Ok(database) = env::var(DATABASE_ENV_VAR) { - let engine = DatabaseEngine::from_str(&database).map_err(|err| anyhow!(err))?; - self.database = Database { engine }; - } - - self.info = self.info.clone().from_env(); - self.mint_info = self.mint_info.clone().from_env(); - self.ln = self.ln.clone().from_env(); - - #[cfg(feature = "management-rpc")] - { - self.mint_management_rpc = Some( - self.mint_management_rpc - .clone() - .unwrap_or_default() - .from_env(), - ); - } - - match self.ln.ln_backend { - LnBackend::Cln => { - self.cln = Some(self.cln.clone().unwrap_or_default().from_env()); - } - LnBackend::LNbits => { - self.lnbits = Some(self.lnbits.clone().unwrap_or_default().from_env()); - } - LnBackend::FakeWallet => { - self.fake_wallet = Some(self.fake_wallet.clone().unwrap_or_default().from_env()); - } - LnBackend::Lnd => { - self.lnd = Some(self.lnd.clone().unwrap_or_default().from_env()); - } - LnBackend::None => bail!("Ln backend must be set"), - } - - Ok(self.clone()) - } -} - -impl Info { - pub fn from_env(mut self) -> Self { - // Required fields - if let Ok(url) = env::var(ENV_URL) { - self.url = url; - } - - if let Ok(host) = env::var(ENV_LISTEN_HOST) { - self.listen_host = host; - } - - if let Ok(port_str) = env::var(ENV_LISTEN_PORT) { - if let Ok(port) = port_str.parse() { - self.listen_port = port; - } - } - - if let Ok(mnemonic) = env::var(ENV_MNEMONIC) { - self.mnemonic = mnemonic; - } - - if let Ok(cache_seconds_str) = env::var(ENV_CACHE_SECONDS) { - if let Ok(seconds) = cache_seconds_str.parse() { - self.http_cache.ttl = Some(seconds); - } - } - - if let Ok(extend_cache_str) = env::var(ENV_EXTEND_CACHE_SECONDS) { - if let Ok(seconds) = extend_cache_str.parse() { - self.http_cache.tti = Some(seconds); - } - } - - if let Ok(fee_str) = env::var(ENV_INPUT_FEE_PPK) { - if let Ok(fee) = fee_str.parse() { - self.input_fee_ppk = Some(fee); - } - } - - if let Ok(swagger_str) = env::var(ENV_ENABLE_SWAGGER) { - if let Ok(enable) = swagger_str.parse() { - self.enable_swagger_ui = Some(enable); - } - } - - self.http_cache = self.http_cache.from_env(); - - self - } -} - -impl MintInfo { - pub fn from_env(mut self) -> Self { - // Required fields - if let Ok(name) = env::var(ENV_MINT_NAME) { - self.name = name; - } - - if let Ok(description) = env::var(ENV_MINT_DESCRIPTION) { - self.description = description; - } - - // Optional fields - if let Ok(pubkey_str) = env::var(ENV_MINT_PUBKEY) { - // Assuming PublicKey has a from_str implementation - if let Ok(pubkey) = pubkey_str.parse() { - self.pubkey = Some(pubkey); - } - } - - if let Ok(desc_long) = env::var(ENV_MINT_DESCRIPTION_LONG) { - self.description_long = Some(desc_long); - } - - if let Ok(icon_url) = env::var(ENV_MINT_ICON_URL) { - self.icon_url = Some(icon_url); - } - - if let Ok(motd) = env::var(ENV_MINT_MOTD) { - self.motd = Some(motd); - } - - if let Ok(nostr_key) = env::var(ENV_MINT_CONTACT_NOSTR) { - self.contact_nostr_public_key = Some(nostr_key); - } - - if let Ok(email) = env::var(ENV_MINT_CONTACT_EMAIL) { - self.contact_email = Some(email); - } - - if let Ok(tos_url) = env::var(ENV_MINT_TOS_URL) { - self.tos_url = Some(tos_url); - } - - self - } -} - -impl Ln { - pub fn from_env(mut self) -> Self { - // LnBackend - if let Ok(backend_str) = env::var(ENV_LN_BACKEND) { - if let Ok(backend) = backend_str.parse() { - self.ln_backend = backend; - } - } - - // Optional invoice description - if let Ok(description) = env::var(ENV_LN_INVOICE_DESCRIPTION) { - self.invoice_description = Some(description); - } - - // Amount fields - if let Ok(min_mint_str) = env::var(ENV_LN_MIN_MINT) { - if let Ok(amount) = min_mint_str.parse::() { - self.min_mint = amount.into(); - } - } - - if let Ok(max_mint_str) = env::var(ENV_LN_MAX_MINT) { - if let Ok(amount) = max_mint_str.parse::() { - self.max_mint = amount.into(); - } - } - - if let Ok(min_melt_str) = env::var(ENV_LN_MIN_MELT) { - if let Ok(amount) = min_melt_str.parse::() { - self.min_melt = amount.into(); - } - } - - if let Ok(max_melt_str) = env::var(ENV_LN_MAX_MELT) { - if let Ok(amount) = max_melt_str.parse::() { - self.max_melt = amount.into(); - } - } - - self - } -} - -impl Cln { - pub fn from_env(mut self) -> Self { - // RPC Path - if let Ok(path) = env::var(ENV_CLN_RPC_PATH) { - self.rpc_path = PathBuf::from(path); - } - - // BOLT12 flag - if let Ok(bolt12_str) = env::var(ENV_CLN_BOLT12) { - if let Ok(bolt12) = bolt12_str.parse() { - self.bolt12 = bolt12; - } - } - - // Fee percent - if let Ok(fee_str) = env::var(ENV_CLN_FEE_PERCENT) { - if let Ok(fee) = fee_str.parse() { - self.fee_percent = fee; - } - } - - // Reserve fee minimum - if let Ok(reserve_fee_str) = env::var(ENV_CLN_RESERVE_FEE_MIN) { - if let Ok(reserve_fee) = reserve_fee_str.parse::() { - self.reserve_fee_min = reserve_fee.into(); - } - } - - self - } -} - -impl Lnd { - pub fn from_env(mut self) -> Self { - if let Ok(address) = env::var(ENV_LND_ADDRESS) { - self.address = address; - } - - if let Ok(cert_path) = env::var(ENV_LND_CERT_FILE) { - self.cert_file = PathBuf::from(cert_path); - } - - if let Ok(macaroon_path) = env::var(ENV_LND_MACAROON_FILE) { - self.macaroon_file = PathBuf::from(macaroon_path); - } - - if let Ok(fee_str) = env::var(ENV_LND_FEE_PERCENT) { - if let Ok(fee) = fee_str.parse() { - self.fee_percent = fee; - } - } - - if let Ok(reserve_fee_str) = env::var(ENV_LND_RESERVE_FEE_MIN) { - if let Ok(reserve_fee) = reserve_fee_str.parse::() { - self.reserve_fee_min = reserve_fee.into(); - } - } - - self - } -} - -impl LNbits { - pub fn from_env(mut self) -> Self { - if let Ok(admin_key) = env::var(ENV_LNBITS_ADMIN_API_KEY) { - self.admin_api_key = admin_key; - } - - if let Ok(invoice_key) = env::var(ENV_LNBITS_INVOICE_API_KEY) { - self.invoice_api_key = invoice_key; - } - - if let Ok(api) = env::var(ENV_LNBITS_API) { - self.lnbits_api = api; - } - - if let Ok(fee_str) = env::var(ENV_LNBITS_FEE_PERCENT) { - if let Ok(fee) = fee_str.parse() { - self.fee_percent = fee; - } - } - - if let Ok(reserve_fee_str) = env::var(ENV_LNBITS_RESERVE_FEE_MIN) { - if let Ok(reserve_fee) = reserve_fee_str.parse::() { - self.reserve_fee_min = reserve_fee.into(); - } - } - - self - } -} - -impl FakeWallet { - pub fn from_env(mut self) -> Self { - // Supported Units - expects comma-separated list - if let Ok(units_str) = env::var(ENV_FAKE_WALLET_SUPPORTED_UNITS) { - if let Ok(units) = units_str - .split(',') - .map(|s| s.trim().parse()) - .collect::, _>>() - { - self.supported_units = units; - } - } - - if let Ok(fee_str) = env::var(ENV_FAKE_WALLET_FEE_PERCENT) { - if let Ok(fee) = fee_str.parse() { - self.fee_percent = fee; - } - } - - if let Ok(reserve_fee_str) = env::var(ENV_FAKE_WALLET_RESERVE_FEE_MIN) { - if let Ok(reserve_fee) = reserve_fee_str.parse::() { - self.reserve_fee_min = reserve_fee.into(); - } - } - - if let Ok(min_delay_str) = env::var(ENV_FAKE_WALLET_MIN_DELAY) { - if let Ok(min_delay) = min_delay_str.parse() { - self.min_delay_time = min_delay; - } - } - - if let Ok(max_delay_str) = env::var(ENV_FAKE_WALLET_MAX_DELAY) { - if let Ok(max_delay) = max_delay_str.parse() { - self.max_delay_time = max_delay; - } - } - - self - } -} - -#[cfg(feature = "management-rpc")] -impl MintManagementRpc { - pub fn from_env(mut self) -> Self { - if let Ok(enabled) = env::var(ENV_MINT_MANAGEMENT_ENABLED) { - if let Ok(enabled) = enabled.parse() { - self.enabled = enabled; - } - } - - if let Ok(address) = env::var(ENV_MINT_MANAGEMENT_ADDRESS) { - self.address = Some(address); - } - - if let Ok(port) = env::var(ENV_MINT_MANAGEMENT_PORT) { - if let Ok(port) = port.parse::() { - self.port = Some(port); - } - } - - if let Ok(tls_path) = env::var(ENV_MINT_MANAGEMENT_TLS_DIR_PATH) { - self.tls_dir_path = Some(tls_path.into()); - } - - self - } -} diff --git a/crates/cdk-mintd/src/env_vars/cln.rs b/crates/cdk-mintd/src/env_vars/cln.rs new file mode 100644 index 00000000..978cf1f8 --- /dev/null +++ b/crates/cdk-mintd/src/env_vars/cln.rs @@ -0,0 +1,44 @@ +//! CLN environment variables + +use std::env; +use std::path::PathBuf; + +use crate::config::Cln; + +// CLN environment variables +pub const ENV_CLN_RPC_PATH: &str = "CDK_MINTD_CLN_RPC_PATH"; +pub const ENV_CLN_BOLT12: &str = "CDK_MINTD_CLN_BOLT12"; +pub const ENV_CLN_FEE_PERCENT: &str = "CDK_MINTD_CLN_FEE_PERCENT"; +pub const ENV_CLN_RESERVE_FEE_MIN: &str = "CDK_MINTD_CLN_RESERVE_FEE_MIN"; + +impl Cln { + pub fn from_env(mut self) -> Self { + // RPC Path + if let Ok(path) = env::var(ENV_CLN_RPC_PATH) { + self.rpc_path = PathBuf::from(path); + } + + // BOLT12 flag + if let Ok(bolt12_str) = env::var(ENV_CLN_BOLT12) { + if let Ok(bolt12) = bolt12_str.parse() { + self.bolt12 = bolt12; + } + } + + // Fee percent + if let Ok(fee_str) = env::var(ENV_CLN_FEE_PERCENT) { + if let Ok(fee) = fee_str.parse() { + self.fee_percent = fee; + } + } + + // Reserve fee minimum + if let Ok(reserve_fee_str) = env::var(ENV_CLN_RESERVE_FEE_MIN) { + if let Ok(reserve_fee) = reserve_fee_str.parse::() { + self.reserve_fee_min = reserve_fee.into(); + } + } + + self + } +} diff --git a/crates/cdk-mintd/src/env_vars/common.rs b/crates/cdk-mintd/src/env_vars/common.rs new file mode 100644 index 00000000..44e5b0e9 --- /dev/null +++ b/crates/cdk-mintd/src/env_vars/common.rs @@ -0,0 +1,13 @@ +//! Common environment variables + +pub const ENV_WORK_DIR: &str = "CDK_MINTD_WORK_DIR"; +pub const DATABASE_ENV_VAR: &str = "CDK_MINTD_DATABASE"; +pub const ENV_URL: &str = "CDK_MINTD_URL"; +pub const ENV_LISTEN_HOST: &str = "CDK_MINTD_LISTEN_HOST"; +pub const ENV_LISTEN_PORT: &str = "CDK_MINTD_LISTEN_PORT"; +pub const ENV_MNEMONIC: &str = "CDK_MINTD_MNEMONIC"; +pub const ENV_SECONDS_QUOTE_VALID: &str = "CDK_MINTD_SECONDS_QUOTE_VALID"; +pub const ENV_CACHE_SECONDS: &str = "CDK_MINTD_CACHE_SECONDS"; +pub const ENV_EXTEND_CACHE_SECONDS: &str = "CDK_MINTD_EXTEND_CACHE_SECONDS"; +pub const ENV_INPUT_FEE_PPK: &str = "CDK_MINTD_INPUT_FEE_PPK"; +pub const ENV_ENABLE_SWAGGER: &str = "CDK_MINTD_ENABLE_SWAGGER"; diff --git a/crates/cdk-mintd/src/env_vars/fake_wallet.rs b/crates/cdk-mintd/src/env_vars/fake_wallet.rs new file mode 100644 index 00000000..f1968915 --- /dev/null +++ b/crates/cdk-mintd/src/env_vars/fake_wallet.rs @@ -0,0 +1,55 @@ +//! FakeWallet environment variables + +use std::env; + +use cdk::nuts::CurrencyUnit; + +use crate::config::FakeWallet; + +// Fake Wallet environment variables +pub const ENV_FAKE_WALLET_SUPPORTED_UNITS: &str = "CDK_MINTD_FAKE_WALLET_SUPPORTED_UNITS"; +pub const ENV_FAKE_WALLET_FEE_PERCENT: &str = "CDK_MINTD_FAKE_WALLET_FEE_PERCENT"; +pub const ENV_FAKE_WALLET_RESERVE_FEE_MIN: &str = "CDK_MINTD_FAKE_WALLET_RESERVE_FEE_MIN"; +pub const ENV_FAKE_WALLET_MIN_DELAY: &str = "CDK_MINTD_FAKE_WALLET_MIN_DELAY"; +pub const ENV_FAKE_WALLET_MAX_DELAY: &str = "CDK_MINTD_FAKE_WALLET_MAX_DELAY"; + +impl FakeWallet { + pub fn from_env(mut self) -> Self { + // Supported Units - expects comma-separated list + if let Ok(units_str) = env::var(ENV_FAKE_WALLET_SUPPORTED_UNITS) { + if let Ok(units) = units_str + .split(',') + .map(|s| s.trim().parse()) + .collect::, _>>() + { + self.supported_units = units; + } + } + + if let Ok(fee_str) = env::var(ENV_FAKE_WALLET_FEE_PERCENT) { + if let Ok(fee) = fee_str.parse() { + self.fee_percent = fee; + } + } + + if let Ok(reserve_fee_str) = env::var(ENV_FAKE_WALLET_RESERVE_FEE_MIN) { + if let Ok(reserve_fee) = reserve_fee_str.parse::() { + self.reserve_fee_min = reserve_fee.into(); + } + } + + if let Ok(min_delay_str) = env::var(ENV_FAKE_WALLET_MIN_DELAY) { + if let Ok(min_delay) = min_delay_str.parse() { + self.min_delay_time = min_delay; + } + } + + if let Ok(max_delay_str) = env::var(ENV_FAKE_WALLET_MAX_DELAY) { + if let Ok(max_delay) = max_delay_str.parse() { + self.max_delay_time = max_delay; + } + } + + self + } +} diff --git a/crates/cdk-mintd/src/env_vars/info.rs b/crates/cdk-mintd/src/env_vars/info.rs new file mode 100644 index 00000000..2bb6ed57 --- /dev/null +++ b/crates/cdk-mintd/src/env_vars/info.rs @@ -0,0 +1,57 @@ +//! Info environment variables + +use std::env; + +use super::common::*; +use crate::config::Info; + +impl Info { + pub fn from_env(mut self) -> Self { + // Required fields + if let Ok(url) = env::var(ENV_URL) { + self.url = url; + } + + if let Ok(host) = env::var(ENV_LISTEN_HOST) { + self.listen_host = host; + } + + if let Ok(port_str) = env::var(ENV_LISTEN_PORT) { + if let Ok(port) = port_str.parse() { + self.listen_port = port; + } + } + + if let Ok(mnemonic) = env::var(ENV_MNEMONIC) { + self.mnemonic = mnemonic; + } + + if let Ok(cache_seconds_str) = env::var(ENV_CACHE_SECONDS) { + if let Ok(seconds) = cache_seconds_str.parse() { + self.http_cache.ttl = Some(seconds); + } + } + + if let Ok(extend_cache_str) = env::var(ENV_EXTEND_CACHE_SECONDS) { + if let Ok(seconds) = extend_cache_str.parse() { + self.http_cache.tti = Some(seconds); + } + } + + if let Ok(fee_str) = env::var(ENV_INPUT_FEE_PPK) { + if let Ok(fee) = fee_str.parse() { + self.input_fee_ppk = Some(fee); + } + } + + if let Ok(swagger_str) = env::var(ENV_ENABLE_SWAGGER) { + if let Ok(enable) = swagger_str.parse() { + self.enable_swagger_ui = Some(enable); + } + } + + self.http_cache = self.http_cache.from_env(); + + self + } +} diff --git a/crates/cdk-mintd/src/env_vars/ln.rs b/crates/cdk-mintd/src/env_vars/ln.rs new file mode 100644 index 00000000..ba76702d --- /dev/null +++ b/crates/cdk-mintd/src/env_vars/ln.rs @@ -0,0 +1,56 @@ +//! Lightning Network common environment variables + +use std::env; + +use crate::config::Ln; + +// LN environment variables +pub const ENV_LN_BACKEND: &str = "CDK_MINTD_LN_BACKEND"; +pub const ENV_LN_INVOICE_DESCRIPTION: &str = "CDK_MINTD_LN_INVOICE_DESCRIPTION"; +pub const ENV_LN_MIN_MINT: &str = "CDK_MINTD_LN_MIN_MINT"; +pub const ENV_LN_MAX_MINT: &str = "CDK_MINTD_LN_MAX_MINT"; +pub const ENV_LN_MIN_MELT: &str = "CDK_MINTD_LN_MIN_MELT"; +pub const ENV_LN_MAX_MELT: &str = "CDK_MINTD_LN_MAX_MELT"; + +impl Ln { + pub fn from_env(mut self) -> Self { + // LnBackend + if let Ok(backend_str) = env::var(ENV_LN_BACKEND) { + if let Ok(backend) = backend_str.parse() { + self.ln_backend = backend; + } + } + + // Optional invoice description + if let Ok(description) = env::var(ENV_LN_INVOICE_DESCRIPTION) { + self.invoice_description = Some(description); + } + + // Amount fields + if let Ok(min_mint_str) = env::var(ENV_LN_MIN_MINT) { + if let Ok(amount) = min_mint_str.parse::() { + self.min_mint = amount.into(); + } + } + + if let Ok(max_mint_str) = env::var(ENV_LN_MAX_MINT) { + if let Ok(amount) = max_mint_str.parse::() { + self.max_mint = amount.into(); + } + } + + if let Ok(min_melt_str) = env::var(ENV_LN_MIN_MELT) { + if let Ok(amount) = min_melt_str.parse::() { + self.min_melt = amount.into(); + } + } + + if let Ok(max_melt_str) = env::var(ENV_LN_MAX_MELT) { + if let Ok(amount) = max_melt_str.parse::() { + self.max_melt = amount.into(); + } + } + + self + } +} diff --git a/crates/cdk-mintd/src/env_vars/lnbits.rs b/crates/cdk-mintd/src/env_vars/lnbits.rs new file mode 100644 index 00000000..2e963ffa --- /dev/null +++ b/crates/cdk-mintd/src/env_vars/lnbits.rs @@ -0,0 +1,42 @@ +//! LNBits environment variables + +use std::env; + +use crate::config::LNbits; + +// LNBits environment variables +pub const ENV_LNBITS_ADMIN_API_KEY: &str = "CDK_MINTD_LNBITS_ADMIN_API_KEY"; +pub const ENV_LNBITS_INVOICE_API_KEY: &str = "CDK_MINTD_LNBITS_INVOICE_API_KEY"; +pub const ENV_LNBITS_API: &str = "CDK_MINTD_LNBITS_API"; +pub const ENV_LNBITS_FEE_PERCENT: &str = "CDK_MINTD_LNBITS_FEE_PERCENT"; +pub const ENV_LNBITS_RESERVE_FEE_MIN: &str = "CDK_MINTD_LNBITS_RESERVE_FEE_MIN"; + +impl LNbits { + pub fn from_env(mut self) -> Self { + if let Ok(admin_key) = env::var(ENV_LNBITS_ADMIN_API_KEY) { + self.admin_api_key = admin_key; + } + + if let Ok(invoice_key) = env::var(ENV_LNBITS_INVOICE_API_KEY) { + self.invoice_api_key = invoice_key; + } + + if let Ok(api) = env::var(ENV_LNBITS_API) { + self.lnbits_api = api; + } + + if let Ok(fee_str) = env::var(ENV_LNBITS_FEE_PERCENT) { + if let Ok(fee) = fee_str.parse() { + self.fee_percent = fee; + } + } + + if let Ok(reserve_fee_str) = env::var(ENV_LNBITS_RESERVE_FEE_MIN) { + if let Ok(reserve_fee) = reserve_fee_str.parse::() { + self.reserve_fee_min = reserve_fee.into(); + } + } + + self + } +} diff --git a/crates/cdk-mintd/src/env_vars/lnd.rs b/crates/cdk-mintd/src/env_vars/lnd.rs new file mode 100644 index 00000000..5949db73 --- /dev/null +++ b/crates/cdk-mintd/src/env_vars/lnd.rs @@ -0,0 +1,43 @@ +//! LND environment variables + +use std::env; +use std::path::PathBuf; + +use crate::config::Lnd; + +// LND environment variables +pub const ENV_LND_ADDRESS: &str = "CDK_MINTD_LND_ADDRESS"; +pub const ENV_LND_CERT_FILE: &str = "CDK_MINTD_LND_CERT_FILE"; +pub const ENV_LND_MACAROON_FILE: &str = "CDK_MINTD_LND_MACAROON_FILE"; +pub const ENV_LND_FEE_PERCENT: &str = "CDK_MINTD_LND_FEE_PERCENT"; +pub const ENV_LND_RESERVE_FEE_MIN: &str = "CDK_MINTD_LND_RESERVE_FEE_MIN"; + +impl Lnd { + pub fn from_env(mut self) -> Self { + if let Ok(address) = env::var(ENV_LND_ADDRESS) { + self.address = address; + } + + if let Ok(cert_path) = env::var(ENV_LND_CERT_FILE) { + self.cert_file = PathBuf::from(cert_path); + } + + if let Ok(macaroon_path) = env::var(ENV_LND_MACAROON_FILE) { + self.macaroon_file = PathBuf::from(macaroon_path); + } + + if let Ok(fee_str) = env::var(ENV_LND_FEE_PERCENT) { + if let Ok(fee) = fee_str.parse() { + self.fee_percent = fee; + } + } + + if let Ok(reserve_fee_str) = env::var(ENV_LND_RESERVE_FEE_MIN) { + if let Ok(reserve_fee) = reserve_fee_str.parse::() { + self.reserve_fee_min = reserve_fee.into(); + } + } + + self + } +} diff --git a/crates/cdk-mintd/src/env_vars/management_rpc.rs b/crates/cdk-mintd/src/env_vars/management_rpc.rs new file mode 100644 index 00000000..387758e7 --- /dev/null +++ b/crates/cdk-mintd/src/env_vars/management_rpc.rs @@ -0,0 +1,37 @@ +//! Management RPC environment variables + +use std::env; + +use crate::config::MintManagementRpc; + +// Mint RPC Server environment variables +pub const ENV_MINT_MANAGEMENT_ENABLED: &str = "CDK_MINTD_MINT_MANAGEMENT_ENABLED"; +pub const ENV_MINT_MANAGEMENT_ADDRESS: &str = "CDK_MINTD_MANAGEMENT_ADDRESS"; +pub const ENV_MINT_MANAGEMENT_PORT: &str = "CDK_MINTD_MANAGEMENT_PORT"; +pub const ENV_MINT_MANAGEMENT_TLS_DIR_PATH: &str = "CDK_MINTD_MANAGEMENT_TLS_DIR_PATH"; + +impl MintManagementRpc { + pub fn from_env(mut self) -> Self { + if let Ok(enabled) = env::var(ENV_MINT_MANAGEMENT_ENABLED) { + if let Ok(enabled) = enabled.parse() { + self.enabled = enabled; + } + } + + if let Ok(address) = env::var(ENV_MINT_MANAGEMENT_ADDRESS) { + self.address = Some(address); + } + + if let Ok(port) = env::var(ENV_MINT_MANAGEMENT_PORT) { + if let Ok(port) = port.parse::() { + self.port = Some(port); + } + } + + if let Ok(tls_path) = env::var(ENV_MINT_MANAGEMENT_TLS_DIR_PATH) { + self.tls_dir_path = Some(tls_path.into()); + } + + self + } +} diff --git a/crates/cdk-mintd/src/env_vars/mint_info.rs b/crates/cdk-mintd/src/env_vars/mint_info.rs new file mode 100644 index 00000000..1367fe66 --- /dev/null +++ b/crates/cdk-mintd/src/env_vars/mint_info.rs @@ -0,0 +1,63 @@ +//! MintInfo environment variables + +use std::env; + +use crate::config::MintInfo; + +// MintInfo environment variables +pub const ENV_MINT_NAME: &str = "CDK_MINTD_MINT_NAME"; +pub const ENV_MINT_PUBKEY: &str = "CDK_MINTD_MINT_PUBKEY"; +pub const ENV_MINT_DESCRIPTION: &str = "CDK_MINTD_MINT_DESCRIPTION"; +pub const ENV_MINT_DESCRIPTION_LONG: &str = "CDK_MINTD_MINT_DESCRIPTION_LONG"; +pub const ENV_MINT_ICON_URL: &str = "CDK_MINTD_MINT_ICON_URL"; +pub const ENV_MINT_MOTD: &str = "CDK_MINTD_MINT_MOTD"; +pub const ENV_MINT_CONTACT_NOSTR: &str = "CDK_MINTD_MINT_CONTACT_NOSTR"; +pub const ENV_MINT_CONTACT_EMAIL: &str = "CDK_MINTD_MINT_CONTACT_EMAIL"; +pub const ENV_MINT_TOS_URL: &str = "CDK_MINTD_MINT_TOS_URL"; + +impl MintInfo { + pub fn from_env(mut self) -> Self { + // Required fields + if let Ok(name) = env::var(ENV_MINT_NAME) { + self.name = name; + } + + if let Ok(description) = env::var(ENV_MINT_DESCRIPTION) { + self.description = description; + } + + // Optional fields + if let Ok(pubkey_str) = env::var(ENV_MINT_PUBKEY) { + // Assuming PublicKey has a from_str implementation + if let Ok(pubkey) = pubkey_str.parse() { + self.pubkey = Some(pubkey); + } + } + + if let Ok(desc_long) = env::var(ENV_MINT_DESCRIPTION_LONG) { + self.description_long = Some(desc_long); + } + + if let Ok(icon_url) = env::var(ENV_MINT_ICON_URL) { + self.icon_url = Some(icon_url); + } + + if let Ok(motd) = env::var(ENV_MINT_MOTD) { + self.motd = Some(motd); + } + + if let Ok(nostr_key) = env::var(ENV_MINT_CONTACT_NOSTR) { + self.contact_nostr_public_key = Some(nostr_key); + } + + if let Ok(email) = env::var(ENV_MINT_CONTACT_EMAIL) { + self.contact_email = Some(email); + } + + if let Ok(tos_url) = env::var(ENV_MINT_TOS_URL) { + self.tos_url = Some(tos_url); + } + + self + } +} diff --git a/crates/cdk-mintd/src/env_vars/mod.rs b/crates/cdk-mintd/src/env_vars/mod.rs new file mode 100644 index 00000000..492595ac --- /dev/null +++ b/crates/cdk-mintd/src/env_vars/mod.rs @@ -0,0 +1,87 @@ +//! Environment variables module +//! +//! This module contains all environment variable definitions and parsing logic +//! organized by component. + +mod common; +mod info; +mod ln; +mod mint_info; + +#[cfg(feature = "cln")] +mod cln; +#[cfg(feature = "fakewallet")] +mod fake_wallet; +#[cfg(feature = "lnbits")] +mod lnbits; +#[cfg(feature = "lnd")] +mod lnd; +#[cfg(feature = "management-rpc")] +mod management_rpc; + +use std::env; +use std::str::FromStr; + +use anyhow::{anyhow, bail, Result}; +#[cfg(feature = "cln")] +pub use cln::*; +pub use common::*; +#[cfg(feature = "fakewallet")] +pub use fake_wallet::*; +pub use ln::*; +#[cfg(feature = "lnbits")] +pub use lnbits::*; +#[cfg(feature = "lnd")] +pub use lnd::*; +#[cfg(feature = "management-rpc")] +pub use management_rpc::*; +pub use mint_info::*; + +use crate::config::{Database, DatabaseEngine, LnBackend, Settings}; + +impl Settings { + pub fn from_env(&mut self) -> Result { + if let Ok(database) = env::var(DATABASE_ENV_VAR) { + let engine = DatabaseEngine::from_str(&database).map_err(|err| anyhow!(err))?; + self.database = Database { engine }; + } + + self.info = self.info.clone().from_env(); + self.mint_info = self.mint_info.clone().from_env(); + self.ln = self.ln.clone().from_env(); + + #[cfg(feature = "management-rpc")] + { + self.mint_management_rpc = Some( + self.mint_management_rpc + .clone() + .unwrap_or_default() + .from_env(), + ); + } + + match self.ln.ln_backend { + #[cfg(feature = "cln")] + LnBackend::Cln => { + self.cln = Some(self.cln.clone().unwrap_or_default().from_env()); + } + #[cfg(feature = "lnbits")] + LnBackend::LNbits => { + self.lnbits = Some(self.lnbits.clone().unwrap_or_default().from_env()); + } + #[cfg(feature = "fakewallet")] + LnBackend::FakeWallet => { + self.fake_wallet = Some(self.fake_wallet.clone().unwrap_or_default().from_env()); + } + #[cfg(feature = "lnd")] + LnBackend::Lnd => { + self.lnd = Some(self.lnd.clone().unwrap_or_default().from_env()); + } + LnBackend::None => bail!("Ln backend must be set"), + #[allow(unreachable_patterns)] + _ => bail!("Selected Ln backend is not enabled in this build"), + } + + Ok(self.clone()) + } +} diff --git a/crates/cdk-mintd/src/lib.rs b/crates/cdk-mintd/src/lib.rs index 1919dcf2..8b6fae30 100644 --- a/crates/cdk-mintd/src/lib.rs +++ b/crates/cdk-mintd/src/lib.rs @@ -1,5 +1,6 @@ //! Cdk mintd lib +#[cfg(feature = "cln")] use std::path::PathBuf; pub mod cli; @@ -7,6 +8,7 @@ pub mod config; pub mod env_vars; pub mod setup; +#[cfg(feature = "cln")] fn expand_path(path: &str) -> Option { if path.starts_with('~') { if let Some(home_dir) = home::home_dir().as_mut() { diff --git a/crates/cdk-mintd/src/main.rs b/crates/cdk-mintd/src/main.rs index f316d099..7fb9ad3e 100644 --- a/crates/cdk-mintd/src/main.rs +++ b/crates/cdk-mintd/src/main.rs @@ -14,6 +14,13 @@ use axum::Router; use bip39::Mnemonic; use cdk::cdk_database::{self, MintDatabase}; use cdk::mint::{MintBuilder, MintMeltLimits}; +// Feature-gated imports +#[cfg(any( + feature = "cln", + feature = "lnbits", + feature = "lnd", + feature = "fakewallet" +))] use cdk::nuts::nut17::SupportedMethods; use cdk::nuts::nut19::{CachedEndpoint, Method as NUT19Method, Path as NUT19Path}; use cdk::nuts::{ContactInfo, CurrencyUnit, MintVersion, PaymentMethod}; @@ -40,6 +47,17 @@ use utoipa::OpenApi; const CARGO_PKG_VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); +// Ensure at least one lightning backend is enabled at compile time +#[cfg(not(any( + feature = "cln", + feature = "lnbits", + feature = "lnd", + feature = "fakewallet" +)))] +compile_error!( + "At least one lightning backend feature must be enabled: cln, lnbits, lnd, or fakewallet" +); + #[tokio::main] async fn main() -> anyhow::Result<()> { let default_filter = "debug"; @@ -149,6 +167,7 @@ async fn main() -> anyhow::Result<()> { }; match settings.ln.ln_backend { + #[cfg(feature = "cln")] LnBackend::Cln => { let cln_settings = settings .cln @@ -171,6 +190,7 @@ async fn main() -> anyhow::Result<()> { mint_builder = mint_builder.add_supported_websockets(nut17_supported); } + #[cfg(feature = "lnbits")] LnBackend::LNbits => { let lnbits_settings = settings.clone().lnbits.expect("Checked on config load"); let lnbits = lnbits_settings @@ -187,6 +207,7 @@ async fn main() -> anyhow::Result<()> { mint_builder = mint_builder.add_supported_websockets(nut17_supported); } + #[cfg(feature = "lnd")] LnBackend::Lnd => { let lnd_settings = settings.clone().lnd.expect("Checked at config load"); let lnd = lnd_settings @@ -204,6 +225,7 @@ async fn main() -> anyhow::Result<()> { mint_builder = mint_builder.add_supported_websockets(nut17_supported); } + #[cfg(feature = "fakewallet")] LnBackend::FakeWallet => { let fake_wallet = settings.clone().fake_wallet.expect("Fake wallet defined"); diff --git a/crates/cdk-mintd/src/setup.rs b/crates/cdk-mintd/src/setup.rs index 0a28fa9b..11e3b695 100644 --- a/crates/cdk-mintd/src/setup.rs +++ b/crates/cdk-mintd/src/setup.rs @@ -1,17 +1,26 @@ -use std::collections::{HashMap, HashSet}; +#[cfg(feature = "fakewallet")] +use std::collections::HashMap; +#[cfg(feature = "fakewallet")] +use std::collections::HashSet; +#[cfg(feature = "lnbits")] use std::sync::Arc; +#[cfg(feature = "cln")] use anyhow::anyhow; use async_trait::async_trait; use axum::Router; +#[cfg(feature = "fakewallet")] use bip39::rand::{thread_rng, Rng}; use cdk::cdk_lightning::MintLightning; use cdk::mint::FeeReserve; +#[cfg(feature = "lnbits")] use cdk::mint_url::MintUrl; use cdk::nuts::CurrencyUnit; +#[cfg(feature = "lnbits")] use tokio::sync::Mutex; use crate::config::{self, Settings}; +#[cfg(feature = "cln")] use crate::expand_path; #[async_trait] @@ -24,6 +33,7 @@ pub trait LnBackendSetup { ) -> anyhow::Result; } +#[cfg(feature = "cln")] #[async_trait] impl LnBackendSetup for config::Cln { async fn setup( @@ -50,6 +60,7 @@ impl LnBackendSetup for config::Cln { } } +#[cfg(feature = "lnbits")] #[async_trait] impl LnBackendSetup for config::LNbits { async fn setup( @@ -93,6 +104,7 @@ impl LnBackendSetup for config::LNbits { } } +#[cfg(feature = "lnd")] #[async_trait] impl LnBackendSetup for config::Lnd { async fn setup( @@ -122,6 +134,7 @@ impl LnBackendSetup for config::Lnd { } } +#[cfg(feature = "fakewallet")] #[async_trait] impl LnBackendSetup for config::FakeWallet { async fn setup(