lib.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. //! CDK lightning backend for LND
  2. // Copyright (c) 2023 Steffen (MIT)
  3. #![doc = include_str!("../README.md")]
  4. #![warn(missing_docs)]
  5. #![warn(rustdoc::bare_urls)]
  6. use std::cmp::max;
  7. use std::path::PathBuf;
  8. use std::pin::Pin;
  9. use std::str::FromStr;
  10. use std::sync::atomic::{AtomicBool, Ordering};
  11. use std::sync::Arc;
  12. use anyhow::anyhow;
  13. use async_trait::async_trait;
  14. use cdk_common::amount::{to_unit, Amount, MSAT_IN_SAT};
  15. use cdk_common::bitcoin::hashes::Hash;
  16. use cdk_common::common::FeeReserve;
  17. use cdk_common::nuts::{CurrencyUnit, MeltOptions, MeltQuoteState};
  18. use cdk_common::payment::{
  19. self, Bolt11Settings, CreateIncomingPaymentResponse, Event, IncomingPaymentOptions,
  20. MakePaymentResponse, MintPayment, OutgoingPaymentOptions, PaymentIdentifier,
  21. PaymentQuoteResponse, WaitPaymentResponse,
  22. };
  23. use cdk_common::util::hex;
  24. use cdk_common::Bolt11Invoice;
  25. use error::Error;
  26. use futures::{Stream, StreamExt};
  27. use lnrpc::fee_limit::Limit;
  28. use lnrpc::payment::PaymentStatus;
  29. use lnrpc::{FeeLimit, Hop, MppRecord};
  30. use tokio_util::sync::CancellationToken;
  31. use tracing::instrument;
  32. mod client;
  33. pub mod error;
  34. mod proto;
  35. pub(crate) use proto::{lnrpc, routerrpc};
  36. use crate::lnrpc::invoice::InvoiceState;
  37. /// Lnd mint backend
  38. #[derive(Clone)]
  39. pub struct Lnd {
  40. _address: String,
  41. _cert_file: PathBuf,
  42. _macaroon_file: PathBuf,
  43. lnd_client: client::Client,
  44. fee_reserve: FeeReserve,
  45. wait_invoice_cancel_token: CancellationToken,
  46. wait_invoice_is_active: Arc<AtomicBool>,
  47. settings: Bolt11Settings,
  48. }
  49. impl Lnd {
  50. /// Maximum number of attempts at a partial payment
  51. pub const MAX_ROUTE_RETRIES: usize = 50;
  52. /// Create new [`Lnd`]
  53. pub async fn new(
  54. address: String,
  55. cert_file: PathBuf,
  56. macaroon_file: PathBuf,
  57. fee_reserve: FeeReserve,
  58. ) -> Result<Self, Error> {
  59. // Validate address is not empty
  60. if address.is_empty() {
  61. return Err(Error::InvalidConfig("LND address cannot be empty".into()));
  62. }
  63. // Validate cert_file exists and is not empty
  64. if !cert_file.exists() || cert_file.metadata().map(|m| m.len() == 0).unwrap_or(true) {
  65. return Err(Error::InvalidConfig(format!(
  66. "LND certificate file not found or empty: {cert_file:?}"
  67. )));
  68. }
  69. // Validate macaroon_file exists and is not empty
  70. if !macaroon_file.exists()
  71. || macaroon_file
  72. .metadata()
  73. .map(|m| m.len() == 0)
  74. .unwrap_or(true)
  75. {
  76. return Err(Error::InvalidConfig(format!(
  77. "LND macaroon file not found or empty: {macaroon_file:?}"
  78. )));
  79. }
  80. let lnd_client = client::connect(&address, &cert_file, &macaroon_file)
  81. .await
  82. .map_err(|err| {
  83. tracing::error!("Connection error: {}", err.to_string());
  84. Error::Connection
  85. })
  86. .unwrap();
  87. Ok(Self {
  88. _address: address,
  89. _cert_file: cert_file,
  90. _macaroon_file: macaroon_file,
  91. lnd_client,
  92. fee_reserve,
  93. wait_invoice_cancel_token: CancellationToken::new(),
  94. wait_invoice_is_active: Arc::new(AtomicBool::new(false)),
  95. settings: Bolt11Settings {
  96. mpp: true,
  97. unit: CurrencyUnit::Msat,
  98. invoice_description: true,
  99. amountless: true,
  100. bolt12: false,
  101. },
  102. })
  103. }
  104. }
  105. #[async_trait]
  106. impl MintPayment for Lnd {
  107. type Err = payment::Error;
  108. #[instrument(skip_all)]
  109. async fn get_settings(&self) -> Result<serde_json::Value, Self::Err> {
  110. Ok(serde_json::to_value(&self.settings)?)
  111. }
  112. #[instrument(skip_all)]
  113. fn is_wait_invoice_active(&self) -> bool {
  114. self.wait_invoice_is_active.load(Ordering::SeqCst)
  115. }
  116. #[instrument(skip_all)]
  117. fn cancel_wait_invoice(&self) {
  118. self.wait_invoice_cancel_token.cancel()
  119. }
  120. #[instrument(skip_all)]
  121. async fn wait_payment_event(
  122. &self,
  123. ) -> Result<Pin<Box<dyn Stream<Item = Event> + Send>>, Self::Err> {
  124. let mut lnd_client = self.lnd_client.clone();
  125. let stream_req = lnrpc::InvoiceSubscription {
  126. add_index: 0,
  127. settle_index: 0,
  128. };
  129. let stream = lnd_client
  130. .lightning()
  131. .subscribe_invoices(stream_req)
  132. .await
  133. .map_err(|_err| {
  134. tracing::error!("Could not subscribe to invoice");
  135. Error::Connection
  136. })?
  137. .into_inner();
  138. let cancel_token = self.wait_invoice_cancel_token.clone();
  139. Ok(futures::stream::unfold(
  140. (
  141. stream,
  142. cancel_token,
  143. Arc::clone(&self.wait_invoice_is_active),
  144. ),
  145. |(mut stream, cancel_token, is_active)| async move {
  146. is_active.store(true, Ordering::SeqCst);
  147. tokio::select! {
  148. _ = cancel_token.cancelled() => {
  149. // Stream is cancelled
  150. is_active.store(false, Ordering::SeqCst);
  151. tracing::info!("Waiting for lnd invoice ending");
  152. None
  153. }
  154. msg = stream.message() => {
  155. match msg {
  156. Ok(Some(msg)) => {
  157. if msg.state() == InvoiceState::Settled {
  158. let hash_slice: Result<[u8;32], _> = msg.r_hash.try_into();
  159. if let Ok(hash_slice) = hash_slice {
  160. let hash = hex::encode(hash_slice);
  161. tracing::info!("LND: Processing payment with hash: {}", hash);
  162. let wait_response = WaitPaymentResponse {
  163. payment_identifier: PaymentIdentifier::PaymentHash(hash_slice), payment_amount: Amount::from(msg.amt_paid_msat as u64),
  164. unit: CurrencyUnit::Msat,
  165. payment_id: hash,
  166. };
  167. tracing::info!("LND: Created WaitPaymentResponse with amount {} msat",
  168. msg.amt_paid_msat);
  169. let event = Event::PaymentReceived(wait_response);
  170. Some((event, (stream, cancel_token, is_active)))
  171. } else { None }
  172. } else {
  173. None
  174. }
  175. }
  176. Ok(None) => {
  177. is_active.store(false, Ordering::SeqCst);
  178. tracing::info!("LND invoice stream ended.");
  179. None
  180. }, // End of stream
  181. Err(err) => {
  182. is_active.store(false, Ordering::SeqCst);
  183. tracing::warn!("Encountered error in LND invoice stream. Stream ending");
  184. tracing::error!("{:?}", err);
  185. None
  186. }, // Handle errors gracefully, ends the stream on error
  187. }
  188. }
  189. }
  190. },
  191. )
  192. .boxed())
  193. }
  194. #[instrument(skip_all)]
  195. async fn get_payment_quote(
  196. &self,
  197. unit: &CurrencyUnit,
  198. options: OutgoingPaymentOptions,
  199. ) -> Result<PaymentQuoteResponse, Self::Err> {
  200. match options {
  201. OutgoingPaymentOptions::Bolt11(bolt11_options) => {
  202. let amount_msat = match bolt11_options.melt_options {
  203. Some(amount) => amount.amount_msat(),
  204. None => bolt11_options
  205. .bolt11
  206. .amount_milli_satoshis()
  207. .ok_or(Error::UnknownInvoiceAmount)?
  208. .into(),
  209. };
  210. let amount = to_unit(amount_msat, &CurrencyUnit::Msat, unit)?;
  211. let relative_fee_reserve =
  212. (self.fee_reserve.percent_fee_reserve * u64::from(amount) as f32) as u64;
  213. let absolute_fee_reserve: u64 = self.fee_reserve.min_fee_reserve.into();
  214. let fee = max(relative_fee_reserve, absolute_fee_reserve);
  215. Ok(PaymentQuoteResponse {
  216. request_lookup_id: Some(PaymentIdentifier::PaymentHash(
  217. *bolt11_options.bolt11.payment_hash().as_ref(),
  218. )),
  219. amount,
  220. fee: fee.into(),
  221. state: MeltQuoteState::Unpaid,
  222. unit: unit.clone(),
  223. })
  224. }
  225. OutgoingPaymentOptions::Bolt12(_) => {
  226. Err(Self::Err::Anyhow(anyhow!("BOLT12 not supported by LND")))
  227. }
  228. }
  229. }
  230. #[instrument(skip_all)]
  231. async fn make_payment(
  232. &self,
  233. _unit: &CurrencyUnit,
  234. options: OutgoingPaymentOptions,
  235. ) -> Result<MakePaymentResponse, Self::Err> {
  236. match options {
  237. OutgoingPaymentOptions::Bolt11(bolt11_options) => {
  238. let bolt11 = bolt11_options.bolt11;
  239. let pay_state = self
  240. .check_outgoing_payment(&PaymentIdentifier::PaymentHash(
  241. *bolt11.payment_hash().as_ref(),
  242. ))
  243. .await?;
  244. match pay_state.status {
  245. MeltQuoteState::Unpaid | MeltQuoteState::Unknown | MeltQuoteState::Failed => (),
  246. MeltQuoteState::Paid => {
  247. tracing::debug!("Melt attempted on invoice already paid");
  248. return Err(Self::Err::InvoiceAlreadyPaid);
  249. }
  250. MeltQuoteState::Pending => {
  251. tracing::debug!("Melt attempted on invoice already pending");
  252. return Err(Self::Err::InvoicePaymentPending);
  253. }
  254. }
  255. // Detect partial payments
  256. match bolt11_options.melt_options {
  257. Some(MeltOptions::Mpp { mpp }) => {
  258. let amount_msat: u64 = bolt11
  259. .amount_milli_satoshis()
  260. .ok_or(Error::UnknownInvoiceAmount)?;
  261. {
  262. let partial_amount_msat = mpp.amount;
  263. let invoice = bolt11;
  264. let max_fee: Option<Amount> = bolt11_options.max_fee_amount;
  265. // Extract information from invoice
  266. let pub_key = invoice.get_payee_pub_key();
  267. let payer_addr = invoice.payment_secret().0.to_vec();
  268. let payment_hash = invoice.payment_hash();
  269. let mut lnd_client = self.lnd_client.clone();
  270. for attempt in 0..Self::MAX_ROUTE_RETRIES {
  271. // Create a request for the routes
  272. let route_req = lnrpc::QueryRoutesRequest {
  273. pub_key: hex::encode(pub_key.serialize()),
  274. amt_msat: u64::from(partial_amount_msat) as i64,
  275. fee_limit: max_fee.map(|f| {
  276. let limit = Limit::Fixed(u64::from(f) as i64);
  277. FeeLimit { limit: Some(limit) }
  278. }),
  279. use_mission_control: true,
  280. ..Default::default()
  281. };
  282. // Query the routes
  283. let mut routes_response = lnd_client
  284. .lightning()
  285. .query_routes(route_req)
  286. .await
  287. .map_err(Error::LndError)?
  288. .into_inner();
  289. // update its MPP record,
  290. // attempt it and check the result
  291. let last_hop: &mut Hop = routes_response.routes[0]
  292. .hops
  293. .last_mut()
  294. .ok_or(Error::MissingLastHop)?;
  295. let mpp_record = MppRecord {
  296. payment_addr: payer_addr.clone(),
  297. total_amt_msat: amount_msat as i64,
  298. };
  299. last_hop.mpp_record = Some(mpp_record);
  300. let payment_response = lnd_client
  301. .router()
  302. .send_to_route_v2(routerrpc::SendToRouteRequest {
  303. payment_hash: payment_hash.to_byte_array().to_vec(),
  304. route: Some(routes_response.routes[0].clone()),
  305. ..Default::default()
  306. })
  307. .await
  308. .map_err(Error::LndError)?
  309. .into_inner();
  310. if let Some(failure) = payment_response.failure {
  311. if failure.code == 15 {
  312. tracing::debug!(
  313. "Attempt number {}: route has failed. Re-querying...",
  314. attempt + 1
  315. );
  316. continue;
  317. }
  318. }
  319. // Get status and maybe the preimage
  320. let (status, payment_preimage) = match payment_response.status {
  321. 0 => (MeltQuoteState::Pending, None),
  322. 1 => (
  323. MeltQuoteState::Paid,
  324. Some(hex::encode(payment_response.preimage)),
  325. ),
  326. 2 => (MeltQuoteState::Unpaid, None),
  327. _ => (MeltQuoteState::Unknown, None),
  328. };
  329. // Get the actual amount paid in sats
  330. let mut total_amt: u64 = 0;
  331. if let Some(route) = payment_response.route {
  332. total_amt = (route.total_amt_msat / 1000) as u64;
  333. }
  334. return Ok(MakePaymentResponse {
  335. payment_lookup_id: PaymentIdentifier::PaymentHash(
  336. payment_hash.to_byte_array(),
  337. ),
  338. payment_proof: payment_preimage,
  339. status,
  340. total_spent: total_amt.into(),
  341. unit: CurrencyUnit::Sat,
  342. });
  343. }
  344. // "We have exhausted all tactical options" -- STEM, Upgrade (2018)
  345. // The payment was not possible within 50 retries.
  346. tracing::error!("Limit of retries reached, payment couldn't succeed.");
  347. Err(Error::PaymentFailed.into())
  348. }
  349. }
  350. _ => {
  351. let mut lnd_client = self.lnd_client.clone();
  352. let max_fee: Option<Amount> = bolt11_options.max_fee_amount;
  353. let amount_msat = u64::from(
  354. bolt11_options
  355. .melt_options
  356. .map(|a| a.amount_msat())
  357. .unwrap_or_default(),
  358. );
  359. let pay_req = lnrpc::SendRequest {
  360. payment_request: bolt11.to_string(),
  361. fee_limit: max_fee.map(|f| {
  362. let limit = Limit::Fixed(u64::from(f) as i64);
  363. FeeLimit { limit: Some(limit) }
  364. }),
  365. amt_msat: amount_msat as i64,
  366. ..Default::default()
  367. };
  368. let payment_response = lnd_client
  369. .lightning()
  370. .send_payment_sync(tonic::Request::new(pay_req))
  371. .await
  372. .map_err(|err| {
  373. tracing::warn!("Lightning payment failed: {}", err);
  374. Error::PaymentFailed
  375. })?
  376. .into_inner();
  377. let total_amount = payment_response
  378. .payment_route
  379. .map_or(0, |route| route.total_amt_msat / MSAT_IN_SAT as i64)
  380. as u64;
  381. let (status, payment_preimage) = match total_amount == 0 {
  382. true => (MeltQuoteState::Unpaid, None),
  383. false => (
  384. MeltQuoteState::Paid,
  385. Some(hex::encode(payment_response.payment_preimage)),
  386. ),
  387. };
  388. let payment_identifier =
  389. PaymentIdentifier::PaymentHash(*bolt11.payment_hash().as_ref());
  390. Ok(MakePaymentResponse {
  391. payment_lookup_id: payment_identifier,
  392. payment_proof: payment_preimage,
  393. status,
  394. total_spent: total_amount.into(),
  395. unit: CurrencyUnit::Sat,
  396. })
  397. }
  398. }
  399. }
  400. OutgoingPaymentOptions::Bolt12(_) => {
  401. Err(Self::Err::Anyhow(anyhow!("BOLT12 not supported by LND")))
  402. }
  403. }
  404. }
  405. #[instrument(skip(self, options))]
  406. async fn create_incoming_payment_request(
  407. &self,
  408. unit: &CurrencyUnit,
  409. options: IncomingPaymentOptions,
  410. ) -> Result<CreateIncomingPaymentResponse, Self::Err> {
  411. match options {
  412. IncomingPaymentOptions::Bolt11(bolt11_options) => {
  413. let description = bolt11_options.description.unwrap_or_default();
  414. let amount = bolt11_options.amount;
  415. let unix_expiry = bolt11_options.unix_expiry;
  416. let amount_msat = to_unit(amount, unit, &CurrencyUnit::Msat)?;
  417. let invoice_request = lnrpc::Invoice {
  418. value_msat: u64::from(amount_msat) as i64,
  419. memo: description,
  420. ..Default::default()
  421. };
  422. let mut lnd_client = self.lnd_client.clone();
  423. let invoice = lnd_client
  424. .lightning()
  425. .add_invoice(tonic::Request::new(invoice_request))
  426. .await
  427. .map_err(|e| payment::Error::Anyhow(anyhow!(e)))?
  428. .into_inner();
  429. let bolt11 = Bolt11Invoice::from_str(&invoice.payment_request)?;
  430. let payment_identifier =
  431. PaymentIdentifier::PaymentHash(*bolt11.payment_hash().as_ref());
  432. Ok(CreateIncomingPaymentResponse {
  433. request_lookup_id: payment_identifier,
  434. request: bolt11.to_string(),
  435. expiry: unix_expiry,
  436. })
  437. }
  438. IncomingPaymentOptions::Bolt12(_) => {
  439. Err(Self::Err::Anyhow(anyhow!("BOLT12 not supported by LND")))
  440. }
  441. }
  442. }
  443. #[instrument(skip(self))]
  444. async fn check_incoming_payment_status(
  445. &self,
  446. payment_identifier: &PaymentIdentifier,
  447. ) -> Result<Vec<WaitPaymentResponse>, Self::Err> {
  448. let mut lnd_client = self.lnd_client.clone();
  449. let invoice_request = lnrpc::PaymentHash {
  450. r_hash: hex::decode(payment_identifier.to_string()).unwrap(),
  451. ..Default::default()
  452. };
  453. let invoice = lnd_client
  454. .lightning()
  455. .lookup_invoice(tonic::Request::new(invoice_request))
  456. .await
  457. .map_err(|e| payment::Error::Anyhow(anyhow!(e)))?
  458. .into_inner();
  459. if invoice.state() == InvoiceState::Settled {
  460. Ok(vec![WaitPaymentResponse {
  461. payment_identifier: payment_identifier.clone(),
  462. payment_amount: Amount::from(invoice.amt_paid_msat as u64),
  463. unit: CurrencyUnit::Msat,
  464. payment_id: hex::encode(invoice.r_hash),
  465. }])
  466. } else {
  467. Ok(vec![])
  468. }
  469. }
  470. #[instrument(skip(self))]
  471. async fn check_outgoing_payment(
  472. &self,
  473. payment_identifier: &PaymentIdentifier,
  474. ) -> Result<MakePaymentResponse, Self::Err> {
  475. let mut lnd_client = self.lnd_client.clone();
  476. let payment_hash = &payment_identifier.to_string();
  477. let track_request = routerrpc::TrackPaymentRequest {
  478. payment_hash: hex::decode(payment_hash).map_err(|_| Error::InvalidHash)?,
  479. no_inflight_updates: true,
  480. };
  481. let payment_response = lnd_client.router().track_payment_v2(track_request).await;
  482. let mut payment_stream = match payment_response {
  483. Ok(stream) => stream.into_inner(),
  484. Err(err) => {
  485. let err_code = err.code();
  486. if err_code == tonic::Code::NotFound {
  487. return Ok(MakePaymentResponse {
  488. payment_lookup_id: payment_identifier.clone(),
  489. payment_proof: None,
  490. status: MeltQuoteState::Unknown,
  491. total_spent: Amount::ZERO,
  492. unit: self.settings.unit.clone(),
  493. });
  494. } else {
  495. return Err(payment::Error::UnknownPaymentState);
  496. }
  497. }
  498. };
  499. while let Some(update_result) = payment_stream.next().await {
  500. match update_result {
  501. Ok(update) => {
  502. let status = update.status();
  503. let response = match status {
  504. PaymentStatus::Unknown => MakePaymentResponse {
  505. payment_lookup_id: payment_identifier.clone(),
  506. payment_proof: Some(update.payment_preimage),
  507. status: MeltQuoteState::Unknown,
  508. total_spent: Amount::ZERO,
  509. unit: self.settings.unit.clone(),
  510. },
  511. PaymentStatus::InFlight | PaymentStatus::Initiated => {
  512. // Continue waiting for the next update
  513. continue;
  514. }
  515. PaymentStatus::Succeeded => MakePaymentResponse {
  516. payment_lookup_id: payment_identifier.clone(),
  517. payment_proof: Some(update.payment_preimage),
  518. status: MeltQuoteState::Paid,
  519. total_spent: Amount::from(
  520. (update
  521. .value_sat
  522. .checked_add(update.fee_sat)
  523. .ok_or(Error::AmountOverflow)?)
  524. as u64,
  525. ),
  526. unit: CurrencyUnit::Sat,
  527. },
  528. PaymentStatus::Failed => MakePaymentResponse {
  529. payment_lookup_id: payment_identifier.clone(),
  530. payment_proof: Some(update.payment_preimage),
  531. status: MeltQuoteState::Failed,
  532. total_spent: Amount::ZERO,
  533. unit: self.settings.unit.clone(),
  534. },
  535. };
  536. return Ok(response);
  537. }
  538. Err(_) => {
  539. // Handle the case where the update itself is an error (e.g., stream failure)
  540. return Err(Error::UnknownPaymentStatus.into());
  541. }
  542. }
  543. }
  544. // If the stream is exhausted without a final status
  545. Err(Error::UnknownPaymentStatus.into())
  546. }
  547. }