1#[cfg(feature = "fakewallet")]
2use std::collections::HashMap;
3#[cfg(feature = "fakewallet")]
4use std::collections::HashSet;
5use std::path::Path;
6use std::sync::Arc;
7
8#[cfg(feature = "cln")]
9use anyhow::anyhow;
10use async_trait::async_trait;
11#[cfg(feature = "fakewallet")]
12use bip39::rand::{thread_rng, Rng};
13use cdk::cdk_database::MintKVStore;
14use cdk::cdk_payment::MintPayment;
15use cdk::nuts::CurrencyUnit;
16#[cfg(any(
17 feature = "lnbits",
18 feature = "cln",
19 feature = "lnd",
20 feature = "ldk-node",
21 feature = "fakewallet"
22))]
23use cdk::types::FeeReserve;
24
25use crate::config::{self, Settings};
26#[cfg(feature = "cln")]
27use crate::expand_path;
28
29#[async_trait]
30pub trait LnBackendSetup {
31 async fn setup(
32 &self,
33 settings: &Settings,
34 unit: CurrencyUnit,
35 runtime: Option<std::sync::Arc<tokio::runtime::Runtime>>,
36 work_dir: &Path,
37 kv_store: Option<Arc<dyn MintKVStore<Err = cdk::cdk_database::Error> + Send + Sync>>,
38 ) -> anyhow::Result<impl MintPayment>;
39}
40
41#[cfg(feature = "cln")]
42#[async_trait]
43impl LnBackendSetup for config::Cln {
44 async fn setup(
45 &self,
46 _settings: &Settings,
47 _unit: CurrencyUnit,
48 _runtime: Option<std::sync::Arc<tokio::runtime::Runtime>>,
49 _work_dir: &Path,
50 kv_store: Option<Arc<dyn MintKVStore<Err = cdk::cdk_database::Error> + Send + Sync>>,
51 ) -> anyhow::Result<cdk_cln::Cln> {
52 let cln_socket = expand_path(
53 self.rpc_path
54 .to_str()
55 .ok_or(anyhow!("cln socket not defined"))?,
56 )
57 .ok_or(anyhow!("cln socket not defined"))?;
58
59 let fee_reserve = FeeReserve {
60 min_fee_reserve: self.reserve_fee_min,
61 percent_fee_reserve: self.fee_percent,
62 };
63
64 let cln = cdk_cln::Cln::new(
65 cln_socket,
66 fee_reserve,
67 kv_store.expect("Cln needs kv store"),
68 )
69 .await?;
70
71 Ok(cln)
72 }
73}
74
75#[cfg(feature = "lnbits")]
76#[async_trait]
77impl LnBackendSetup for config::LNbits {
78 async fn setup(
79 &self,
80 _settings: &Settings,
81 _unit: CurrencyUnit,
82 _runtime: Option<std::sync::Arc<tokio::runtime::Runtime>>,
83 _work_dir: &Path,
84 _kv_store: Option<Arc<dyn MintKVStore<Err = cdk::cdk_database::Error> + Send + Sync>>,
85 ) -> anyhow::Result<cdk_lnbits::LNbits> {
86 let admin_api_key = &self.admin_api_key;
87 let invoice_api_key = &self.invoice_api_key;
88
89 let fee_reserve = FeeReserve {
90 min_fee_reserve: self.reserve_fee_min,
91 percent_fee_reserve: self.fee_percent,
92 };
93
94 let lnbits = cdk_lnbits::LNbits::new(
95 admin_api_key.clone(),
96 invoice_api_key.clone(),
97 self.lnbits_api.clone(),
98 fee_reserve,
99 )
100 .await?;
101
102 lnbits.subscribe_ws().await?;
104
105 Ok(lnbits)
106 }
107}
108
109#[cfg(feature = "lnd")]
110#[async_trait]
111impl LnBackendSetup for config::Lnd {
112 async fn setup(
113 &self,
114 _settings: &Settings,
115 _unit: CurrencyUnit,
116 _runtime: Option<std::sync::Arc<tokio::runtime::Runtime>>,
117 _work_dir: &Path,
118 kv_store: Option<Arc<dyn MintKVStore<Err = cdk::cdk_database::Error> + Send + Sync>>,
119 ) -> anyhow::Result<cdk_lnd::Lnd> {
120 let address = &self.address;
121 let cert_file = &self.cert_file;
122 let macaroon_file = &self.macaroon_file;
123
124 let fee_reserve = FeeReserve {
125 min_fee_reserve: self.reserve_fee_min,
126 percent_fee_reserve: self.fee_percent,
127 };
128
129 let lnd = cdk_lnd::Lnd::new(
130 address.to_string(),
131 cert_file.clone(),
132 macaroon_file.clone(),
133 fee_reserve,
134 kv_store.expect("Lnd needs kv store"),
135 )
136 .await?;
137
138 Ok(lnd)
139 }
140}
141
142#[cfg(feature = "fakewallet")]
143#[async_trait]
144impl LnBackendSetup for config::FakeWallet {
145 async fn setup(
146 &self,
147 _settings: &Settings,
148 unit: CurrencyUnit,
149 _runtime: Option<std::sync::Arc<tokio::runtime::Runtime>>,
150 _work_dir: &Path,
151 _kv_store: Option<Arc<dyn MintKVStore<Err = cdk::cdk_database::Error> + Send + Sync>>,
152 ) -> anyhow::Result<cdk_fake_wallet::FakeWallet> {
153 let fee_reserve = FeeReserve {
154 min_fee_reserve: self.reserve_fee_min,
155 percent_fee_reserve: self.fee_percent,
156 };
157
158 let mut rng = thread_rng();
160 let delay_time = rng.gen_range(self.min_delay_time..=self.max_delay_time);
161
162 let fake_wallet = cdk_fake_wallet::FakeWallet::new(
163 fee_reserve,
164 HashMap::default(),
165 HashSet::default(),
166 delay_time,
167 unit,
168 );
169
170 Ok(fake_wallet)
171 }
172}
173
174#[cfg(feature = "grpc-processor")]
175#[async_trait]
176impl LnBackendSetup for config::GrpcProcessor {
177 async fn setup(
178 &self,
179 _settings: &Settings,
180 _unit: CurrencyUnit,
181 _runtime: Option<std::sync::Arc<tokio::runtime::Runtime>>,
182 _work_dir: &Path,
183 _kv_store: Option<Arc<dyn MintKVStore<Err = cdk::cdk_database::Error> + Send + Sync>>,
184 ) -> anyhow::Result<cdk_payment_processor::PaymentProcessorClient> {
185 let payment_processor = cdk_payment_processor::PaymentProcessorClient::new(
186 &self.addr,
187 self.port,
188 self.tls_dir.clone(),
189 )
190 .await?;
191
192 Ok(payment_processor)
193 }
194}
195
196#[cfg(feature = "ldk-node")]
197#[async_trait]
198impl LnBackendSetup for config::LdkNode {
199 async fn setup(
200 &self,
201 _settings: &Settings,
202 _unit: CurrencyUnit,
203 runtime: Option<std::sync::Arc<tokio::runtime::Runtime>>,
204 work_dir: &Path,
205 _kv_store: Option<Arc<dyn MintKVStore<Err = cdk::cdk_database::Error> + Send + Sync>>,
206 ) -> anyhow::Result<cdk_ldk_node::CdkLdkNode> {
207 use std::net::SocketAddr;
208
209 use bitcoin::Network;
210
211 let fee_reserve = FeeReserve {
212 min_fee_reserve: self.reserve_fee_min,
213 percent_fee_reserve: self.fee_percent,
214 };
215
216 let network = match self
218 .bitcoin_network
219 .as_ref()
220 .map(|n| n.to_lowercase())
221 .as_deref()
222 .unwrap_or("regtest")
223 {
224 "mainnet" | "bitcoin" => Network::Bitcoin,
225 "testnet" => Network::Testnet,
226 "signet" => Network::Signet,
227 _ => Network::Regtest,
228 };
229
230 let chain_source = match self
232 .chain_source_type
233 .as_ref()
234 .map(|s| s.to_lowercase())
235 .as_deref()
236 .unwrap_or("esplora")
237 {
238 "bitcoinrpc" => {
239 let host = self
240 .bitcoind_rpc_host
241 .clone()
242 .unwrap_or_else(|| "127.0.0.1".to_string());
243 let port = self.bitcoind_rpc_port.unwrap_or(18443);
244 let user = self
245 .bitcoind_rpc_user
246 .clone()
247 .unwrap_or_else(|| "testuser".to_string());
248 let password = self
249 .bitcoind_rpc_password
250 .clone()
251 .unwrap_or_else(|| "testpass".to_string());
252
253 cdk_ldk_node::ChainSource::BitcoinRpc(cdk_ldk_node::BitcoinRpcConfig {
254 host,
255 port,
256 user,
257 password,
258 })
259 }
260 _ => {
261 let esplora_url = self
262 .esplora_url
263 .clone()
264 .unwrap_or_else(|| "https://mutinynet.com/api".to_string());
265 cdk_ldk_node::ChainSource::Esplora(esplora_url)
266 }
267 };
268
269 let gossip_source = match self.rgs_url.clone() {
271 Some(rgs_url) => cdk_ldk_node::GossipSource::RapidGossipSync(rgs_url),
272 None => cdk_ldk_node::GossipSource::P2P,
273 };
274
275 let storage_dir_path = if let Some(dir_path) = &self.storage_dir_path {
277 dir_path.clone()
278 } else {
279 let mut work_dir = work_dir.to_path_buf();
280 work_dir.push("ldk-node");
281 work_dir.to_string_lossy().to_string()
282 };
283
284 let host = self
286 .ldk_node_host
287 .clone()
288 .unwrap_or_else(|| "127.0.0.1".to_string());
289 let port = self.ldk_node_port.unwrap_or(8090);
290
291 let socket_addr = SocketAddr::new(host.parse()?, port);
292
293 let listen_address = vec![socket_addr.into()];
297
298 let mut ldk_node = cdk_ldk_node::CdkLdkNode::new(
299 network,
300 chain_source,
301 gossip_source,
302 storage_dir_path,
303 fee_reserve,
304 listen_address,
305 runtime,
306 )?;
307
308 let webserver_addr = if let Some(host) = &self.webserver_host {
310 let port = self.webserver_port.unwrap_or(8091);
311 let socket_addr: SocketAddr = format!("{host}:{port}").parse()?;
312 Some(socket_addr)
313 } else if self.webserver_port.is_some() {
314 let port = self.webserver_port.unwrap_or(8091);
316 let socket_addr: SocketAddr = format!("127.0.0.1:{port}").parse()?;
317 Some(socket_addr)
318 } else {
319 Some(cdk_ldk_node::CdkLdkNode::default_web_addr())
321 };
322
323 println!("webserver: {:?}", webserver_addr);
324
325 ldk_node.set_web_addr(webserver_addr);
326
327 Ok(ldk_node)
328 }
329}