Skip to main content

fedimint_gateway_server/
config.rs

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