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