1use std::net::SocketAddr;
2use std::path::PathBuf;
3use std::str::FromStr;
4
5use bitcoin::Network;
6use clap::{ArgGroup, Parser};
7use fedimint_core::envs::{FM_IROH_DNS_ENV, FM_IROH_RELAY_ENV};
8use fedimint_core::util::SafeUrl;
9use fedimint_gateway_common::{LightningMode, V1_API_ENDPOINT};
10use fedimint_lnv2_common::gateway_api::PaymentFee;
11
12use super::envs;
13use crate::envs::{
14 FM_BITCOIND_PASSWORD_ENV, FM_BITCOIND_URL_ENV, FM_BITCOIND_USERNAME_ENV, FM_ESPLORA_URL_ENV,
15 FM_GATEWAY_METRICS_LISTEN_ADDR_ENV, FM_GATEWAY_SKIP_SETUP_ENV,
16};
17
18#[derive(Debug, Clone, Copy, clap::ValueEnum)]
19pub enum DatabaseBackend {
20 #[value(name = "rocksdb")]
22 RocksDb,
23 #[value(name = "cursed-redb")]
25 CursedRedb,
26}
27
28#[derive(Parser)]
31#[command(version)]
32#[command(
33 group(
34 ArgGroup::new("bitcoind_password_auth")
35 .args(["bitcoind_password"])
36 .multiple(false)
37 ),
38 group(
39 ArgGroup::new("bitcoind_auth")
40 .args(["bitcoind_url"])
41 .requires("bitcoind_password_auth")
42 .requires_all(["bitcoind_username", "bitcoind_url"])
43 ),
44 group(
45 ArgGroup::new("bitcoin_rpc")
46 .required(true)
47 .multiple(true)
48 .args(["bitcoind_url", "esplora_url"])
49 )
50)]
51pub struct GatewayOpts {
52 #[clap(subcommand)]
53 pub mode: LightningMode,
54
55 #[arg(long = "data-dir", env = envs::FM_GATEWAY_DATA_DIR_ENV)]
57 pub data_dir: PathBuf,
58
59 #[arg(long = "listen", env = envs::FM_GATEWAY_LISTEN_ADDR_ENV)]
61 listen: SocketAddr,
62
63 #[arg(long = "api-addr", env = envs::FM_GATEWAY_API_ADDR_ENV)]
65 api_addr: Option<SafeUrl>,
66
67 #[arg(long = "bcrypt-password-hash", env = envs::FM_GATEWAY_BCRYPT_PASSWORD_HASH_ENV)]
69 bcrypt_password_hash: String,
70
71 #[arg(long = "bcrypt_liquidity_manager_password_hash", env = envs::FM_GATEWAY_LIQUIDITY_MANAGER_BCRYPT_PASSWORD_HASH_ENV)]
74 bcrypt_liquidity_manager_password_hash: Option<String>,
75
76 #[arg(long = "network", env = envs::FM_GATEWAY_NETWORK_ENV)]
78 network: Network,
79
80 #[arg(
82 long = "num-route-hints",
83 env = envs::FM_NUMBER_OF_ROUTE_HINTS_ENV,
84 default_value_t = super::DEFAULT_NUM_ROUTE_HINTS
85 )]
86 num_route_hints: u32,
87
88 #[arg(long, env = envs::FM_DB_BACKEND_ENV, value_enum, default_value = "rocksdb")]
90 pub db_backend: DatabaseBackend,
91
92 #[arg(long, env = FM_BITCOIND_USERNAME_ENV)]
94 pub bitcoind_username: Option<String>,
95
96 #[arg(long, env = FM_BITCOIND_PASSWORD_ENV)]
98 pub bitcoind_password: Option<String>,
99
100 #[arg(long, env = FM_BITCOIND_URL_ENV)]
104 pub bitcoind_url: Option<SafeUrl>,
105
106 #[arg(long, env = FM_ESPLORA_URL_ENV)]
108 pub esplora_url: Option<SafeUrl>,
109
110 #[arg(long = "default-routing-fees", env = envs::FM_DEFAULT_ROUTING_FEES_ENV, default_value_t = PaymentFee::TRANSACTION_FEE_DEFAULT)]
112 default_routing_fees: PaymentFee,
113
114 #[arg(long = "default-transaction-fees", env = envs::FM_DEFAULT_TRANSACTION_FEES_ENV, default_value_t = PaymentFee::TRANSACTION_FEE_DEFAULT)]
116 default_transaction_fees: PaymentFee,
117
118 #[arg(long = "iroh-listen", env = envs::FM_GATEWAY_IROH_LISTEN_ADDR_ENV)]
120 iroh_listen: Option<SocketAddr>,
121
122 #[arg(long = "metrics-listen", env = FM_GATEWAY_METRICS_LISTEN_ADDR_ENV)]
125 metrics_listen: Option<SocketAddr>,
126
127 #[arg(long, env = FM_IROH_DNS_ENV)]
129 iroh_dns: Option<SafeUrl>,
130
131 #[arg(long, env = FM_IROH_RELAY_ENV, value_delimiter = ',')]
133 iroh_relays: Vec<SafeUrl>,
134
135 #[arg(long, env = FM_GATEWAY_SKIP_SETUP_ENV, default_value_t = false)]
136 skip_setup: bool,
137}
138
139impl GatewayOpts {
140 pub fn to_gateway_parameters(&self) -> anyhow::Result<GatewayParameters> {
143 let versioned_api = self.api_addr.clone().map(|api_addr| {
144 api_addr
145 .join(V1_API_ENDPOINT)
146 .expect("Could not join v1 api_addr")
147 });
148 let bcrypt_password_hash = bcrypt::HashParts::from_str(&self.bcrypt_password_hash)?;
149 let bcrypt_liquidity_manager_password_hash =
150 if let Some(h) = &self.bcrypt_liquidity_manager_password_hash {
151 Some(bcrypt::HashParts::from_str(h)?)
152 } else {
153 None
154 };
155
156 let metrics_listen = self.metrics_listen.unwrap_or_else(|| {
158 SocketAddr::new(
159 std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
160 self.listen.port() + 1,
161 )
162 });
163
164 Ok(GatewayParameters {
165 listen: self.listen,
166 versioned_api,
167 bcrypt_password_hash,
168 bcrypt_liquidity_manager_password_hash,
169 network: self.network,
170 num_route_hints: self.num_route_hints,
171 default_routing_fees: self.default_routing_fees,
172 default_transaction_fees: self.default_transaction_fees,
173 iroh_listen: self.iroh_listen,
174 iroh_dns: self.iroh_dns.clone(),
175 iroh_relays: self.iroh_relays.clone(),
176 skip_setup: self.skip_setup,
177 metrics_listen,
178 })
179 }
180}
181
182#[derive(Debug)]
189pub struct GatewayParameters {
190 pub listen: SocketAddr,
191 pub versioned_api: Option<SafeUrl>,
192 pub bcrypt_password_hash: bcrypt::HashParts,
193 pub bcrypt_liquidity_manager_password_hash: Option<bcrypt::HashParts>,
194 pub network: Network,
195 pub num_route_hints: u32,
196 pub default_routing_fees: PaymentFee,
197 pub default_transaction_fees: PaymentFee,
198 pub iroh_listen: Option<SocketAddr>,
199 pub iroh_dns: Option<SafeUrl>,
200 pub iroh_relays: Vec<SafeUrl>,
201 pub skip_setup: bool,
202 pub metrics_listen: SocketAddr,
203}