Skip to main content

fedimint_core/
envs.rs

1use std::collections::BTreeMap;
2use std::str::FromStr;
3use std::{cmp, env};
4
5use anyhow::Context;
6use fedimint_core::util::SafeUrl;
7use fedimint_derive::{Decodable, Encodable};
8use fedimint_logging::LOG_CORE;
9use jsonrpsee_core::Serialize;
10use serde::Deserialize;
11use tracing::warn;
12
13use crate::util::FmtCompact as _;
14
15/// In tests we want to routinely enable an extra unknown module to ensure
16/// all client code handles correct modules that client doesn't know about.
17pub const FM_USE_UNKNOWN_MODULE_ENV: &str = "FM_USE_UNKNOWN_MODULE";
18
19pub const FM_ENABLE_MODULE_LNV1_ENV: &str = "FM_ENABLE_MODULE_LNV1";
20pub const FM_ENABLE_MODULE_LNV2_ENV: &str = "FM_ENABLE_MODULE_LNV2";
21pub const FM_ENABLE_MODULE_WALLET_ENV: &str = "FM_ENABLE_MODULE_WALLET";
22pub const FM_ENABLE_MODULE_WALLETV2_ENV: &str = "FM_ENABLE_MODULE_WALLETV2";
23
24/// Disable mint base fees for testing and development environments
25pub const FM_DISABLE_BASE_FEES_ENV: &str = "FM_DISABLE_BASE_FEES";
26
27/// Print sensitive secrets without redacting them. Use only for debugging.
28pub const FM_DEBUG_SHOW_SECRETS_ENV: &str = "FM_DEBUG_SHOW_SECRETS";
29
30/// Check if env variable is set and not equal `0` or `false` which are common
31/// ways to disable something.
32pub fn is_env_var_set(var: &str) -> bool {
33    let Some(val) = std::env::var_os(var) else {
34        return false;
35    };
36    match val.as_encoded_bytes() {
37        b"0" | b"false" => false,
38        b"1" | b"true" => true,
39        _ => {
40            warn!(
41                target: LOG_CORE,
42                %var,
43                val = %val.to_string_lossy(),
44                "Env var value invalid is invalid and ignored, assuming `true`"
45            );
46            true
47        }
48    }
49}
50
51/// Check if env variable is set and not equal `0` or `false` which are common
52/// ways to disable a setting. `None` if env var not set at all, which allows
53/// handling the default value.
54pub fn is_env_var_set_opt(var: &str) -> Option<bool> {
55    let val = std::env::var_os(var)?;
56    match val.as_encoded_bytes() {
57        b"0" | b"false" => Some(false),
58        b"1" | b"true" => Some(true),
59        _ => {
60            warn!(
61                target: LOG_CORE,
62                %var,
63                val = %val.to_string_lossy(),
64                "Env var value invalid is invalid and ignored"
65            );
66            None
67        }
68    }
69}
70
71/// Use to detect if running in a test environment, either `cargo test` or
72/// `devimint`.
73pub fn is_running_in_test_env() -> bool {
74    let unit_test = cfg!(test);
75
76    unit_test || is_env_var_set("NEXTEST") || is_env_var_set(FM_IN_DEVIMINT_ENV)
77}
78
79/// Use to allow `process_output` to process RBF withdrawal outputs.
80pub fn is_rbf_withdrawal_enabled() -> bool {
81    is_env_var_set("FM_UNSAFE_ENABLE_RBF_WITHDRAWAL")
82}
83
84/// Get value of `FEDIMINT_BUILD_CODE_VERSION` at compile time
85#[macro_export]
86macro_rules! fedimint_build_code_version_env {
87    () => {
88        env!("FEDIMINT_BUILD_CODE_VERSION")
89    };
90}
91
92/// Env var for bitcoin RPC kind (obsolete, use FM_DEFAULT_* instead)
93pub const FM_BITCOIN_RPC_KIND_ENV: &str = "FM_BITCOIN_RPC_KIND";
94/// Env var for bitcoin URL (obsolete, use FM_DEFAULT_* instead)
95pub const FM_BITCOIN_RPC_URL_ENV: &str = "FM_BITCOIN_RPC_URL";
96/// Env var how often to poll bitcoin source
97pub const FM_BITCOIN_POLLING_INTERVAL_SECS_ENV: &str = "FM_BITCOIN_POLLING_INTERVAL_SECS";
98
99/// Env var for bitcoin RPC kind (default, used only as a default value for DKG
100/// config settings)
101pub const FM_DEFAULT_BITCOIN_RPC_KIND_ENV: &str = "FM_DEFAULT_BITCOIN_RPC_KIND";
102pub const FM_DEFAULT_BITCOIN_RPC_KIND_BAD_ENV: &str = "FM_DEFAULT_BITCOIND_RPC_KIND";
103/// Env var for bitcoin URL (default, used only as a default value for DKG
104/// config settings)
105pub const FM_DEFAULT_BITCOIN_RPC_URL_ENV: &str = "FM_DEFAULT_BITCOIN_RPC_URL";
106pub const FM_DEFAULT_BITCOIN_RPC_URL_BAD_ENV: &str = "FM_DEFAULT_BITCOIND_RPC_URL";
107
108/// Env var for bitcoin RPC kind (forced, takes priority over config settings)
109pub const FM_FORCE_BITCOIN_RPC_KIND_ENV: &str = "FM_FORCE_BITCOIN_RPC_KIND";
110pub const FM_FORCE_BITCOIN_RPC_KIND_BAD_ENV: &str = "FM_FORCE_BITCOIND_RPC_BAD_KIND";
111/// Env var for bitcoin URL (default, takes priority over config settings)
112pub const FM_FORCE_BITCOIN_RPC_URL_ENV: &str = "FM_FORCE_BITCOIN_RPC_URL";
113pub const FM_FORCE_BITCOIN_RPC_URL_BAD_ENV: &str = "FM_FORCE_BITCOIND_RPC_URL";
114
115/// Env var to override iroh connectivity
116///
117/// Comma separated key-value list (`<node_id>=<ticket>,<node_id>=<ticket>,...`)
118pub const FM_IROH_CONNECT_OVERRIDES_ENV: &str = "FM_IROH_CONNECT_OVERRIDES";
119
120/// Env var to override iroh connectivity
121///
122/// Comma separated key-value list (`<node_id>=<ticket>,<node_id>=<ticket>,...`)
123pub const FM_GW_IROH_CONNECT_OVERRIDES_ENV: &str = "FM_GW_IROH_CONNECT_OVERRIDES";
124
125/// Env var to override iroh DNS server
126pub const FM_IROH_DNS_ENV: &str = "FM_IROH_DNS";
127
128/// Env var to override iroh relays server
129pub const FM_IROH_RELAY_ENV: &str = "FM_IROH_RELAY";
130
131/// Env var to disable Iroh's use of DHT
132pub const FM_IROH_DHT_ENABLE_ENV: &str = "FM_IROH_DHT_ENABLE";
133
134/// Env var to disable default n0 discovery
135pub const FM_IROH_N0_DISCOVERY_ENABLE_ENV: &str = "FM_IROH_N0_DISCOVERY_ENABLE";
136
137/// Env var to disable default pkarr resolver
138pub const FM_IROH_PKARR_RESOLVER_ENABLE_ENV: &str = "FM_IROH_PKARR_RESOLVER_ENABLE";
139
140/// Env var to disable default pkarr publisher
141pub const FM_IROH_PKARR_PUBLISHER_ENABLE_ENV: &str = "FM_IROH_PKARR_PUBLISHER_ENABLE";
142
143/// Env var to disable Iroh's use of relays
144pub const FM_IROH_RELAYS_ENABLE_ENV: &str = "FM_IROH_RELAYS_ENABLE";
145
146/// Env var to disable all pkarr publishing (enabled by default)
147pub const FM_PKARR_ENABLE_ENV: &str = "FM_PKARR_ENABLE";
148
149/// Env var to enable pkarr DHT publishing (disabled by default)
150pub const FM_PKARR_DHT_ENABLE_ENV: &str = "FM_PKARR_DHT_ENABLE";
151
152/// Env var to disable pkarr relay publishing (enabled by default)
153pub const FM_PKARR_RELAYS_ENABLE_ENV: &str = "FM_PKARR_RELAYS_ENABLE";
154
155/// Env var to override tcp api connectivity
156///
157/// Comma separated key-value list (`peer_id=url,peer_id=url`)
158pub const FM_WS_API_CONNECT_OVERRIDES_ENV: &str = "FM_WS_API_CONNECT_OVERRIDES";
159
160pub const FM_IROH_API_SECRET_KEY_OVERRIDE_ENV: &str = "FM_IROH_API_SECRET_KEY_OVERRIDE";
161pub const FM_IROH_P2P_SECRET_KEY_OVERRIDE_ENV: &str = "FM_IROH_P2P_SECRET_KEY_OVERRIDE";
162
163/// List of json api endpoint sources to use as a source of
164/// fee rate estimation.
165///
166/// `;`-separated list of urls with part after `#`
167/// ("fragment") specifying jq filter to extract sats/vB fee rate.
168/// Eg. `https://mempool.space/api/v1/fees/recommended#.halfHourFee`
169///
170/// Note that `#` is a standalone separator and *not* parsed as a part of the
171/// Url. Which means there's no need to escape it.
172pub const FM_WALLET_FEERATE_SOURCES_ENV: &str = "FM_WALLET_FEERATE_SOURCES";
173
174/// `devimint` will set when code is running inside `devimint`
175pub const FM_IN_DEVIMINT_ENV: &str = "FM_IN_DEVIMINT";
176
177/// Configuration for the bitcoin RPC
178#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
179pub struct BitcoinRpcConfig {
180    pub kind: String,
181    pub url: SafeUrl,
182}
183
184impl BitcoinRpcConfig {
185    pub fn get_defaults_from_env_vars() -> anyhow::Result<Self> {
186        Ok(Self {
187        kind: env::var(FM_FORCE_BITCOIN_RPC_KIND_ENV)
188            .or_else(|_| env::var(FM_DEFAULT_BITCOIN_RPC_KIND_ENV))
189            .or_else(|_| env::var(FM_BITCOIN_RPC_KIND_ENV).inspect(|_v| {
190                warn!(target: LOG_CORE, "{FM_BITCOIN_RPC_KIND_ENV} is obsolete, use {FM_DEFAULT_BITCOIN_RPC_KIND_ENV} instead");
191            }))
192            .or_else(|_| env::var(FM_FORCE_BITCOIN_RPC_KIND_BAD_ENV).inspect(|_v| {
193                warn!(target: LOG_CORE, "{FM_FORCE_BITCOIN_RPC_KIND_BAD_ENV} is obsolete, use {FM_FORCE_BITCOIN_RPC_KIND_ENV} instead");
194            }))
195            .or_else(|_| env::var(FM_DEFAULT_BITCOIN_RPC_KIND_BAD_ENV).inspect(|_v| {
196                warn!(target: LOG_CORE, "{FM_DEFAULT_BITCOIN_RPC_KIND_BAD_ENV} is obsolete, use {FM_DEFAULT_BITCOIN_RPC_KIND_ENV} instead");
197            }))
198            .with_context(|| {
199                anyhow::anyhow!("failure looking up env var for Bitcoin RPC kind")
200            })?,
201        url: env::var(FM_FORCE_BITCOIN_RPC_URL_ENV)
202            .or_else(|_| env::var(FM_DEFAULT_BITCOIN_RPC_URL_ENV))
203            .or_else(|_| env::var(FM_BITCOIN_RPC_URL_ENV).inspect(|_v| {
204                warn!(target: LOG_CORE, "{FM_BITCOIN_RPC_URL_ENV} is obsolete, use {FM_DEFAULT_BITCOIN_RPC_URL_ENV} instead");
205            }))
206            .or_else(|_| env::var(FM_FORCE_BITCOIN_RPC_URL_BAD_ENV).inspect(|_v| {
207                warn!(target: LOG_CORE, "{FM_FORCE_BITCOIN_RPC_URL_BAD_ENV} is obsolete, use {FM_FORCE_BITCOIN_RPC_URL_ENV} instead");
208            }))
209            .or_else(|_| env::var(FM_DEFAULT_BITCOIN_RPC_URL_BAD_ENV).inspect(|_v| {
210                warn!(target: LOG_CORE, "{FM_DEFAULT_BITCOIN_RPC_URL_BAD_ENV} is obsolete, use {FM_DEFAULT_BITCOIN_RPC_URL_ENV} instead");
211            }))
212            .with_context(|| {
213                anyhow::anyhow!("failure looking up env var for Bitcoin RPC URL")
214            })?
215            .parse()
216            .with_context(|| {
217                anyhow::anyhow!("failure parsing Bitcoin RPC URL")
218            })?,
219    })
220    }
221}
222
223pub fn parse_kv_list_from_env<K, V>(env: &str) -> anyhow::Result<BTreeMap<K, V>>
224where
225    K: FromStr + cmp::Ord,
226    <K as FromStr>::Err: std::error::Error,
227    V: FromStr,
228    <V as FromStr>::Err: std::error::Error,
229{
230    let mut map = BTreeMap::new();
231    let Ok(env_value) = std::env::var(env) else {
232        return Ok(BTreeMap::new());
233    };
234    for kv in env_value.split(',') {
235        let kv = kv.trim();
236
237        if kv.is_empty() {
238            continue;
239        }
240
241        if let Some((k, v)) = kv.split_once('=') {
242            let Some(k) = K::from_str(k)
243                .inspect_err(|err| {
244                    warn!(
245                        target: LOG_CORE,
246                        err = %err.fmt_compact(),
247                        "Error parsing value"
248                    );
249                })
250                .ok()
251            else {
252                continue;
253            };
254            let Some(v) = V::from_str(v)
255                .inspect_err(|err| {
256                    warn!(
257                        target: LOG_CORE,
258                        err = %err.fmt_compact(),
259                        "Error parsing value"
260                    );
261                })
262                .ok()
263            else {
264                continue;
265            };
266
267            map.insert(k, v);
268        }
269    }
270
271    Ok(map)
272}