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 #[value(name = "rocksdb")]
24 RocksDb,
25 #[value(name = "cursed-redb")]
27 CursedRedb,
28}
29
30#[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 #[arg(long = "data-dir", env = envs::FM_GATEWAY_DATA_DIR_ENV)]
59 pub data_dir: PathBuf,
60
61 #[arg(long = "listen", env = envs::FM_GATEWAY_LISTEN_ADDR_ENV)]
63 listen: SocketAddr,
64
65 #[arg(long = "api-addr", env = envs::FM_GATEWAY_API_ADDR_ENV)]
67 api_addr: Option<SafeUrl>,
68
69 #[arg(long = "bcrypt-password-hash", env = envs::FM_GATEWAY_BCRYPT_PASSWORD_HASH_ENV)]
71 bcrypt_password_hash: String,
72
73 #[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 #[arg(long = "network", env = envs::FM_GATEWAY_NETWORK_ENV)]
80 network: Network,
81
82 #[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 #[arg(long, env = envs::FM_DB_BACKEND_ENV, value_enum, default_value = "rocksdb")]
92 pub db_backend: DatabaseBackend,
93
94 #[arg(long, env = FM_BITCOIND_USERNAME_ENV)]
96 pub bitcoind_username: Option<String>,
97
98 #[arg(long, env = FM_BITCOIND_PASSWORD_ENV)]
100 pub bitcoind_password: Option<String>,
101
102 #[arg(long, env = FM_BITCOIND_URL_ENV)]
106 pub bitcoind_url: Option<SafeUrl>,
107
108 #[arg(long, env = FM_ESPLORA_URL_ENV)]
110 pub esplora_url: Option<SafeUrl>,
111
112 #[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 #[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 #[arg(long = "iroh-listen", env = envs::FM_GATEWAY_IROH_LISTEN_ADDR_ENV)]
122 iroh_listen: Option<SocketAddr>,
123
124 #[arg(long = "metrics-listen", env = FM_GATEWAY_METRICS_LISTEN_ADDR_ENV)]
127 metrics_listen: Option<SocketAddr>,
128
129 #[arg(long, env = FM_IROH_DNS_ENV)]
131 iroh_dns: Option<SafeUrl>,
132
133 #[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 #[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 #[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 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 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 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#[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}