lib.rs 31 KB

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