fedimint_gateway_server/
config.rs

1use std::fmt::Display;
2use std::net::SocketAddr;
3use std::path::PathBuf;
4use std::str::FromStr;
5
6use bitcoin::Network;
7use clap::{ArgGroup, Parser};
8use fedimint_core::util::SafeUrl;
9use fedimint_gateway_common::{LightningMode, V1_API_ENDPOINT};
10
11use super::envs;
12use crate::envs::{
13    FM_BITCOIND_PASSWORD_ENV, FM_BITCOIND_URL_ENV, FM_BITCOIND_USERNAME_ENV, FM_ESPLORA_URL_ENV,
14};
15
16#[derive(Debug, Clone, Copy, clap::ValueEnum)]
17pub enum DatabaseBackend {
18    /// Use RocksDB database backend
19    #[value(name = "rocksdb")]
20    RocksDb,
21    /// Use CursedRedb database backend (hybrid memory/redb)
22    #[value(name = "cursed-redb")]
23    CursedRedb,
24}
25
26/// Command line parameters for starting the gateway. `mode`, `data_dir`,
27/// `listen`, and `api_addr` are all required.
28#[derive(Parser)]
29#[command(version)]
30#[command(
31    group(
32        ArgGroup::new("bitcoind_password_auth")
33           .args(["bitcoind_password"])
34           .multiple(false)
35    ),
36    group(
37        ArgGroup::new("bitcoind_auth")
38            .args(["bitcoind_url"])
39            .requires("bitcoind_password_auth")
40            .requires_all(["bitcoind_username", "bitcoind_url"])
41    ),
42    group(
43        ArgGroup::new("bitcoin_rpc")
44            .required(true)
45            .multiple(true)
46            .args(["bitcoind_url", "esplora_url"])
47    )
48)]
49pub struct GatewayOpts {
50    #[clap(subcommand)]
51    pub mode: LightningMode,
52
53    /// Path to folder containing gateway config and data files
54    #[arg(long = "data-dir", env = envs::FM_GATEWAY_DATA_DIR_ENV)]
55    pub data_dir: PathBuf,
56
57    /// Gateway webserver listen address
58    #[arg(long = "listen", env = envs::FM_GATEWAY_LISTEN_ADDR_ENV)]
59    listen: SocketAddr,
60
61    /// Public URL from which the webserver API is reachable
62    #[arg(long = "api-addr", env = envs::FM_GATEWAY_API_ADDR_ENV)]
63    api_addr: SafeUrl,
64
65    /// Gateway webserver authentication bcrypt password hash
66    #[arg(long = "bcrypt-password-hash", env = envs::FM_GATEWAY_BCRYPT_PASSWORD_HASH_ENV)]
67    bcrypt_password_hash: String,
68
69    /// Bitcoin network this gateway will be running on
70    #[arg(long = "network", env = envs::FM_GATEWAY_NETWORK_ENV)]
71    network: Network,
72
73    /// Number of route hints to return in invoices
74    #[arg(
75        long = "num-route-hints",
76        env = envs::FM_NUMBER_OF_ROUTE_HINTS_ENV,
77        default_value_t = super::DEFAULT_NUM_ROUTE_HINTS
78    )]
79    num_route_hints: u32,
80
81    /// The Lightning module to use: LNv1, LNv2, or both
82    #[arg(long = "lightning-module-mode", env = envs::FM_GATEWAY_LIGHTNING_MODULE_MODE_ENV, default_value_t = LightningModuleMode::LNv1)]
83    lightning_module_mode: LightningModuleMode,
84
85    /// Database backend to use.
86    #[arg(long, env = envs::FM_DB_BACKEND_ENV, value_enum, default_value = "rocksdb")]
87    pub db_backend: DatabaseBackend,
88
89    /// The username to use when connecting to bitcoind
90    #[arg(long, env = FM_BITCOIND_USERNAME_ENV)]
91    pub bitcoind_username: Option<String>,
92
93    /// The password to use when connecting to bitcoind
94    #[arg(long, env = FM_BITCOIND_PASSWORD_ENV)]
95    pub bitcoind_password: Option<String>,
96
97    /// Bitcoind RPC URL, e.g. <http://127.0.0.1:8332>
98    /// This should not include authentication parameters, they should be
99    /// included in `FM_BITCOIND_USERNAME` and `FM_BITCOIND_PASSWORD`
100    #[arg(long, env = FM_BITCOIND_URL_ENV)]
101    pub bitcoind_url: Option<SafeUrl>,
102
103    /// Esplora HTTP base URL, e.g. <https://mempool.space/api>
104    #[arg(long, env = FM_ESPLORA_URL_ENV)]
105    pub esplora_url: Option<SafeUrl>,
106}
107
108impl GatewayOpts {
109    /// Converts the command line parameters into a helper struct the Gateway
110    /// uses to store runtime parameters.
111    pub fn to_gateway_parameters(&self) -> anyhow::Result<GatewayParameters> {
112        let versioned_api = self.api_addr.join(V1_API_ENDPOINT).map_err(|e| {
113            anyhow::anyhow!(
114                "Failed to version gateway API address: {api_addr:?}, error: {e:?}",
115                api_addr = self.api_addr,
116            )
117        })?;
118
119        let bcrypt_password_hash = bcrypt::HashParts::from_str(&self.bcrypt_password_hash)?;
120
121        Ok(GatewayParameters {
122            listen: self.listen,
123            versioned_api,
124            bcrypt_password_hash,
125            network: self.network,
126            num_route_hints: self.num_route_hints,
127            lightning_module_mode: self.lightning_module_mode,
128        })
129    }
130}
131
132/// `GatewayParameters` is a helper struct that can be derived from
133/// `GatewayOpts` that holds the CLI or environment variables that are specified
134/// by the user.
135///
136/// If `GatewayConfiguration is set in the database, that takes precedence and
137/// the optional parameters will have no affect.
138#[derive(Debug)]
139pub struct GatewayParameters {
140    pub listen: SocketAddr,
141    pub versioned_api: SafeUrl,
142    pub bcrypt_password_hash: bcrypt::HashParts,
143    pub network: Network,
144    pub num_route_hints: u32,
145    pub lightning_module_mode: LightningModuleMode,
146}
147
148#[derive(Debug, Clone, Copy, Eq, PartialEq)]
149pub enum LightningModuleMode {
150    LNv1,
151    LNv2,
152    All,
153}
154
155impl Display for LightningModuleMode {
156    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157        match self {
158            LightningModuleMode::LNv1 => write!(f, "LNv1"),
159            LightningModuleMode::LNv2 => write!(f, "LNv2"),
160            LightningModuleMode::All => write!(f, "All"),
161        }
162    }
163}
164
165impl FromStr for LightningModuleMode {
166    type Err = anyhow::Error;
167
168    fn from_str(s: &str) -> Result<Self, Self::Err> {
169        let mode = match s {
170            "LNv1" => LightningModuleMode::LNv1,
171            "LNv2" => LightningModuleMode::LNv2,
172            _ => LightningModuleMode::All,
173        };
174
175        Ok(mode)
176    }
177}