Skip to main content

cdk_mintd/env_vars/
mod.rs

1#![allow(missing_docs)]
2//! Environment variables module
3//!
4//! This module contains all environment variable definitions and parsing logic
5//! organized by component.
6
7mod common;
8mod database;
9mod info;
10mod limits;
11mod mint_info;
12mod onchain;
13mod payment_backend;
14mod signatory;
15
16mod auth;
17#[cfg(feature = "bdk")]
18mod bdk;
19#[cfg(feature = "cln")]
20mod cln;
21#[cfg(feature = "fakewallet")]
22mod fake_wallet;
23#[cfg(feature = "grpc-processor")]
24mod grpc_processor;
25#[cfg(feature = "ldk-node")]
26mod ldk_node;
27#[cfg(feature = "lnd")]
28mod lnd;
29#[cfg(feature = "management-rpc")]
30mod management_rpc;
31#[cfg(feature = "prometheus")]
32mod prometheus;
33
34use std::env;
35use std::str::FromStr;
36
37use anyhow::{anyhow, bail, Result};
38pub use auth::*;
39#[cfg(feature = "bdk")]
40pub use bdk::*;
41#[cfg(feature = "cln")]
42pub use cln::*;
43pub use common::*;
44pub use database::*;
45#[cfg(feature = "fakewallet")]
46pub use fake_wallet::*;
47#[cfg(feature = "grpc-processor")]
48pub use grpc_processor::*;
49#[cfg(feature = "ldk-node")]
50pub use ldk_node::*;
51pub use limits::*;
52#[cfg(feature = "lnd")]
53pub use lnd::*;
54#[cfg(feature = "management-rpc")]
55pub use management_rpc::*;
56pub use mint_info::*;
57pub use onchain::*;
58pub use payment_backend::*;
59#[cfg(feature = "prometheus")]
60pub use prometheus::*;
61
62use crate::config::{DatabaseEngine, OnchainBackend, PaymentBackend, PaymentBackendType, Settings};
63
64impl Settings {
65    pub fn from_env(&mut self) -> Result<Self> {
66        if let Ok(database) = env::var(DATABASE_ENV_VAR) {
67            let engine = DatabaseEngine::from_str(&database).map_err(|err| anyhow!(err))?;
68            self.database.engine = engine;
69        }
70
71        // Parse PostgreSQL-specific configuration from environment variables
72        if self.database.engine == DatabaseEngine::Postgres {
73            self.database.postgres = Some(
74                self.database
75                    .postgres
76                    .clone()
77                    .unwrap_or_default()
78                    .from_env(),
79            );
80        }
81
82        // Parse auth database configuration from environment variables
83        self.auth_database = Some(crate::config::AuthDatabase {
84            postgres: Some(
85                self.auth_database
86                    .clone()
87                    .unwrap_or_default()
88                    .postgres
89                    .unwrap_or_default()
90                    .from_env(),
91            ),
92        });
93
94        self.info = self.info.clone().from_env();
95        self.signatory = Some(self.signatory.clone().unwrap_or_default().from_env());
96
97        self.mint_info = self.mint_info.clone().from_env();
98        // CDK_MINTD_PAYMENT_BACKEND_* env vars only apply when there is exactly
99        // one configured payment backend entry. Multi-backend setups must
100        // choose units and backends in the config file so env overrides do not
101        // collapse them.
102        match self.payment_backend.len() {
103            0 => {
104                let backend = PaymentBackend::default().from_env();
105                if backend.backend != PaymentBackendType::None {
106                    self.payment_backend.push(backend);
107                }
108            }
109            1 => {
110                self.payment_backend[0] = self.payment_backend[0].clone().from_env();
111            }
112            _ => {
113                tracing::warn!(
114                    "CDK_MINTD_PAYMENT_BACKEND_* environment variables ignored: multiple [[payment_backend]] entries configured"
115                );
116            }
117        }
118        self.onchain = Some(self.onchain.clone().unwrap_or_default().from_env());
119        self.limits = self.limits.clone().from_env();
120
121        {
122            // Check env vars for auth config even if None
123            let auth = self.auth.clone().unwrap_or_default().from_env();
124
125            // Only set auth if auth_enabled flag is true
126            if auth.auth_enabled {
127                self.auth = Some(auth);
128            } else {
129                self.auth = None;
130            }
131        }
132
133        #[cfg(feature = "management-rpc")]
134        {
135            self.mint_management_rpc = Some(
136                self.mint_management_rpc
137                    .clone()
138                    .unwrap_or_default()
139                    .from_env(),
140            );
141        }
142
143        #[cfg(feature = "prometheus")]
144        {
145            self.prometheus = Some(self.prometheus.clone().unwrap_or_default().from_env());
146        }
147
148        #[cfg(feature = "cln")]
149        {
150            let cln = self.cln.clone().unwrap_or_default().from_env();
151            if cln.rpc_path.as_os_str().is_empty() {
152                self.cln = None;
153            } else {
154                self.cln = Some(cln);
155            }
156        }
157
158        #[cfg(feature = "fakewallet")]
159        {
160            // Fake wallet has defaults so it is always Some if feature enabled
161            let fake_wallet_supported_units_from_env =
162                env::var(ENV_FAKE_WALLET_SUPPORTED_UNITS).is_ok();
163            let fake_wallet = self.fake_wallet.clone().unwrap_or_default().from_env();
164            let supported_units_configured =
165                fake_wallet.supported_units != vec![cdk::nuts::CurrencyUnit::Sat];
166
167            if fake_wallet_supported_units_from_env || supported_units_configured {
168                self.expand_single_fake_wallet_backend_entry(&fake_wallet);
169            }
170
171            self.fake_wallet = Some(fake_wallet);
172        }
173
174        #[cfg(feature = "lnd")]
175        {
176            let lnd = self.lnd.clone().unwrap_or_default().from_env()?;
177            if lnd.address.is_empty() {
178                self.lnd = None;
179            } else {
180                self.lnd = Some(lnd);
181            }
182        }
183
184        #[cfg(feature = "ldk-node")]
185        {
186            let ldk_node = self.ldk_node.clone().unwrap_or_default().from_env();
187            if ldk_node.bitcoin_network.is_none()
188                && ldk_node.esplora_url.is_none()
189                && ldk_node.electrum_url.is_none()
190            {
191                self.ldk_node = None;
192            } else {
193                self.ldk_node = Some(ldk_node);
194            }
195        }
196
197        #[cfg(feature = "grpc-processor")]
198        {
199            let grpc_processor = self.grpc_processor.clone().unwrap_or_default().from_env();
200            let grpc_processor_configured = self
201                .payment_backend
202                .iter()
203                .any(|backend| backend.backend == PaymentBackendType::GrpcProcessor);
204            if grpc_processor.supported_units.is_empty() && !grpc_processor_configured {
205                self.grpc_processor = None;
206            } else {
207                self.grpc_processor = Some(grpc_processor);
208            }
209        }
210
211        #[cfg(feature = "bdk")]
212        {
213            let bdk = self.bdk.clone().unwrap_or_default().from_env();
214            if bdk.network.is_none() && bdk.mnemonic.is_none() {
215                self.bdk = None;
216            } else {
217                self.bdk = Some(bdk);
218            }
219        }
220
221        for payment_backend in &self.payment_backend {
222            match payment_backend.backend {
223                #[cfg(feature = "cln")]
224                PaymentBackendType::Cln => {}
225                #[cfg(feature = "fakewallet")]
226                PaymentBackendType::FakeWallet => {}
227                #[cfg(feature = "lnd")]
228                PaymentBackendType::Lnd => {}
229                #[cfg(feature = "ldk-node")]
230                PaymentBackendType::LdkNode => {}
231                #[cfg(feature = "grpc-processor")]
232                PaymentBackendType::GrpcProcessor => {}
233                PaymentBackendType::None => {}
234                #[allow(unreachable_patterns)]
235                _ => bail!("Selected payment backend is not enabled in this build"),
236            }
237        }
238
239        let has_payment_backend = self
240            .payment_backend
241            .iter()
242            .any(|backend| backend.backend != PaymentBackendType::None);
243        let has_onchain_backend = self
244            .onchain
245            .as_ref()
246            .map(|onchain| onchain.onchain_backend != OnchainBackend::None)
247            .unwrap_or(false);
248        if !has_payment_backend && !has_onchain_backend {
249            bail!("At least one payment backend must be set");
250        }
251
252        self.validate_backend_pairing()
253            .map_err(|err| anyhow!(err))?;
254
255        Ok(self.clone())
256    }
257
258    #[cfg(feature = "fakewallet")]
259    fn expand_single_fake_wallet_backend_entry(&mut self, fake_wallet: &crate::config::FakeWallet) {
260        let fake_wallet_backend_index = self
261            .payment_backend
262            .iter()
263            .enumerate()
264            .filter_map(|(index, backend)| {
265                (backend.backend == PaymentBackendType::FakeWallet).then_some(index)
266            })
267            .collect::<Vec<_>>();
268
269        if fake_wallet_backend_index.len() != 1 {
270            return;
271        }
272
273        let mut units = Vec::new();
274        for unit in &fake_wallet.supported_units {
275            if !units.contains(unit) {
276                units.push(unit.clone());
277            }
278        }
279
280        if units.is_empty() {
281            return;
282        }
283
284        let index = fake_wallet_backend_index[0];
285        let base_backend = self.payment_backend[index].clone();
286        let expanded_backends = units.into_iter().map(|unit| PaymentBackend {
287            unit,
288            ..base_backend.clone()
289        });
290
291        self.payment_backend
292            .splice(index..=index, expanded_backends);
293    }
294}