Skip to main content

fedimint_gateway_server/
config.rs

1use std::net::SocketAddr;
2use std::path::PathBuf;
3use std::str::FromStr;
4
5use bitcoin::Network;
6use clap::builder::BoolishValueParser;
7use clap::{ArgGroup, Parser};
8use fedimint_core::envs::{FM_IROH_DNS_ENV, FM_IROH_RELAY_ENV};
9use fedimint_core::util::SafeUrl;
10use fedimint_gateway_common::{LightningMode, V1_API_ENDPOINT};
11use fedimint_lnv2_common::gateway_api::PaymentFee;
12
13use super::envs;
14use crate::envs::{
15    FM_BITCOIND_PASSWORD_ENV, FM_BITCOIND_URL_ENV, FM_BITCOIND_USERNAME_ENV, FM_ESPLORA_URL_ENV,
16    FM_GATEWAY_METRICS_LISTEN_ADDR_ENV, FM_GATEWAY_SKIP_SETUP_ENV,
17};
18
19#[derive(Debug, Clone, Copy, clap::ValueEnum)]
20pub enum DatabaseBackend {
21    /// Use RocksDB database backend
22    #[value(name = "rocksdb")]
23    RocksDb,
24    /// Use CursedRedb database backend (hybrid memory/redb)
25    #[value(name = "cursed-redb")]
26    CursedRedb,
27}
28
29/// Command line parameters for starting the gateway. `mode`, `data_dir`,
30/// `listen`, and `api_addr` are all required.
31#[derive(Parser)]
32#[command(version)]
33#[command(
34    group(
35        ArgGroup::new("bitcoind_password_auth")
36           .args(["bitcoind_password"])
37           .multiple(false)
38    ),
39    group(
40        ArgGroup::new("bitcoind_auth")
41            .args(["bitcoind_url"])
42            .requires("bitcoind_password_auth")
43            .requires_all(["bitcoind_username", "bitcoind_url"])
44    ),
45    group(
46        ArgGroup::new("bitcoin_rpc")
47            .required(true)
48            .multiple(true)
49            .args(["bitcoind_url", "esplora_url"])
50    )
51)]
52pub struct GatewayOpts {
53    #[clap(subcommand)]
54    pub mode: LightningMode,
55
56    /// Path to folder containing gateway config and data files
57    #[arg(long = "data-dir", env = envs::FM_GATEWAY_DATA_DIR_ENV)]
58    pub data_dir: PathBuf,
59
60    /// Gateway webserver listen address
61    #[arg(long = "listen", env = envs::FM_GATEWAY_LISTEN_ADDR_ENV)]
62    listen: SocketAddr,
63
64    /// Public URL from which the webserver API is reachable
65    #[arg(long = "api-addr", env = envs::FM_GATEWAY_API_ADDR_ENV)]
66    api_addr: Option<SafeUrl>,
67
68    /// Gateway webserver authentication bcrypt password hash
69    #[arg(long = "bcrypt-password-hash", env = envs::FM_GATEWAY_BCRYPT_PASSWORD_HASH_ENV)]
70    bcrypt_password_hash: String,
71
72    /// Gateway liquidity manager for channel and liquidity management
73    /// operations
74    #[arg(long = "bcrypt_liquidity_manager_password_hash", env = envs::FM_GATEWAY_LIQUIDITY_MANAGER_BCRYPT_PASSWORD_HASH_ENV)]
75    bcrypt_liquidity_manager_password_hash: Option<String>,
76
77    /// Bitcoin network this gateway will be running on
78    #[arg(long = "network", env = envs::FM_GATEWAY_NETWORK_ENV)]
79    network: Network,
80
81    /// Number of route hints to return in invoices
82    #[arg(
83        long = "num-route-hints",
84        env = envs::FM_NUMBER_OF_ROUTE_HINTS_ENV,
85        default_value_t = super::DEFAULT_NUM_ROUTE_HINTS
86    )]
87    num_route_hints: u32,
88
89    /// Database backend to use.
90    #[arg(long, env = envs::FM_DB_BACKEND_ENV, value_enum, default_value = "rocksdb")]
91    pub db_backend: DatabaseBackend,
92
93    /// The username to use when connecting to bitcoind
94    #[arg(long, env = FM_BITCOIND_USERNAME_ENV)]
95    pub bitcoind_username: Option<String>,
96
97    /// The password to use when connecting to bitcoind
98    #[arg(long, env = FM_BITCOIND_PASSWORD_ENV)]
99    pub bitcoind_password: Option<String>,
100
101    /// Bitcoind RPC URL, e.g. <http://127.0.0.1:8332>
102    /// This should not include authentication parameters, they should be
103    /// included in `FM_BITCOIND_USERNAME` and `FM_BITCOIND_PASSWORD`
104    #[arg(long, env = FM_BITCOIND_URL_ENV)]
105    pub bitcoind_url: Option<SafeUrl>,
106
107    /// Esplora HTTP base URL, e.g. <https://mempool.space/api>
108    #[arg(long, env = FM_ESPLORA_URL_ENV)]
109    pub esplora_url: Option<SafeUrl>,
110
111    /// The default routing fees that are applied to new federations
112    #[arg(long = "default-routing-fees", env = envs::FM_DEFAULT_ROUTING_FEES_ENV, default_value_t = PaymentFee::TRANSACTION_FEE_DEFAULT)]
113    default_routing_fees: PaymentFee,
114
115    /// The default transaction fees that are applied to new federations
116    #[arg(long = "default-transaction-fees", env = envs::FM_DEFAULT_TRANSACTION_FEES_ENV, default_value_t = PaymentFee::TRANSACTION_FEE_DEFAULT)]
117    default_transaction_fees: PaymentFee,
118
119    /// Gateway iroh listen address
120    #[arg(long = "iroh-listen", env = envs::FM_GATEWAY_IROH_LISTEN_ADDR_ENV)]
121    iroh_listen: Option<SocketAddr>,
122
123    /// Gateway metrics listen address. If not set, defaults to localhost on the
124    /// UI port + 1.
125    #[arg(long = "metrics-listen", env = FM_GATEWAY_METRICS_LISTEN_ADDR_ENV)]
126    metrics_listen: Option<SocketAddr>,
127
128    /// Optional URL of the Iroh DNS server
129    #[arg(long, env = FM_IROH_DNS_ENV)]
130    iroh_dns: Option<SafeUrl>,
131
132    /// Optional URLs of the Iroh relays to use for registering
133    #[arg(long, env = FM_IROH_RELAY_ENV, value_delimiter = ',')]
134    iroh_relays: Vec<SafeUrl>,
135
136    #[arg(
137        long,
138        env = FM_GATEWAY_SKIP_SETUP_ENV,
139        default_value_t = false,
140        value_parser = BoolishValueParser::new()
141    )]
142    skip_setup: bool,
143}
144
145impl GatewayOpts {
146    /// Converts the command line parameters into a helper struct the Gateway
147    /// uses to store runtime parameters.
148    pub fn to_gateway_parameters(&self) -> anyhow::Result<GatewayParameters> {
149        let versioned_api = self.api_addr.clone().map(|api_addr| {
150            api_addr
151                .join(V1_API_ENDPOINT)
152                .expect("Could not join v1 api_addr")
153        });
154        let bcrypt_password_hash = bcrypt::HashParts::from_str(&self.bcrypt_password_hash)?;
155        let bcrypt_liquidity_manager_password_hash =
156            if let Some(h) = &self.bcrypt_liquidity_manager_password_hash {
157                Some(bcrypt::HashParts::from_str(h)?)
158            } else {
159                None
160            };
161
162        // Default metrics listen to localhost on UI port + 1
163        let metrics_listen = self.metrics_listen.unwrap_or_else(|| {
164            SocketAddr::new(
165                std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
166                self.listen.port() + 1,
167            )
168        });
169
170        Ok(GatewayParameters {
171            listen: self.listen,
172            versioned_api,
173            bcrypt_password_hash,
174            bcrypt_liquidity_manager_password_hash,
175            network: self.network,
176            num_route_hints: self.num_route_hints,
177            default_routing_fees: self.default_routing_fees,
178            default_transaction_fees: self.default_transaction_fees,
179            iroh_listen: self.iroh_listen,
180            iroh_dns: self.iroh_dns.clone(),
181            iroh_relays: self.iroh_relays.clone(),
182            skip_setup: self.skip_setup,
183            metrics_listen,
184        })
185    }
186}
187
188/// `GatewayParameters` is a helper struct that can be derived from
189/// `GatewayOpts` that holds the CLI or environment variables that are specified
190/// by the user.
191///
192/// If `GatewayConfiguration is set in the database, that takes precedence and
193/// the optional parameters will have no affect.
194#[derive(Debug)]
195pub struct GatewayParameters {
196    pub listen: SocketAddr,
197    pub versioned_api: Option<SafeUrl>,
198    pub bcrypt_password_hash: bcrypt::HashParts,
199    pub bcrypt_liquidity_manager_password_hash: Option<bcrypt::HashParts>,
200    pub network: Network,
201    pub num_route_hints: u32,
202    pub default_routing_fees: PaymentFee,
203    pub default_transaction_fees: PaymentFee,
204    pub iroh_listen: Option<SocketAddr>,
205    pub iroh_dns: Option<SafeUrl>,
206    pub iroh_relays: Vec<SafeUrl>,
207    pub skip_setup: bool,
208    pub metrics_listen: SocketAddr,
209}