lib.rs 31 KB

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