1use crate::config::{Config, LDK_PAYMENT_RETRY_TIMEOUT};
13use crate::connection::ConnectionManager;
14use crate::data_store::DataStoreUpdateResult;
15use crate::error::Error;
16use crate::liquidity::LiquiditySource;
17use crate::logger::{log_error, log_info, LdkLogger, Logger};
18use crate::payment::store::{
19 LSPFeeLimits, PaymentDetails, PaymentDetailsUpdate, PaymentDirection, PaymentKind,
20 PaymentStatus,
21};
22use crate::payment::SendingParameters;
23use crate::peer_store::{PeerInfo, PeerStore};
24use crate::types::{ChannelManager, PaymentStore};
25
26use lightning::ln::bolt11_payment;
27use lightning::ln::channelmanager::{
28 Bolt11InvoiceParameters, PaymentId, RecipientOnionFields, Retry, RetryableSendFailure,
29};
30use lightning::routing::router::{PaymentParameters, RouteParameters};
31
32use lightning_types::payment::{PaymentHash, PaymentPreimage};
33
34use lightning_invoice::Bolt11Invoice as LdkBolt11Invoice;
35use lightning_invoice::Bolt11InvoiceDescription as LdkBolt11InvoiceDescription;
36
37use bitcoin::hashes::sha256::Hash as Sha256;
38use bitcoin::hashes::Hash;
39
40use std::sync::{Arc, RwLock};
41
42#[cfg(not(feature = "uniffi"))]
43type Bolt11Invoice = LdkBolt11Invoice;
44#[cfg(feature = "uniffi")]
45type Bolt11Invoice = Arc<crate::uniffi_types::Bolt11Invoice>;
46
47#[cfg(not(feature = "uniffi"))]
48pub(crate) fn maybe_wrap_invoice(invoice: LdkBolt11Invoice) -> Bolt11Invoice {
49 invoice
50}
51#[cfg(feature = "uniffi")]
52pub(crate) fn maybe_wrap_invoice(invoice: LdkBolt11Invoice) -> Bolt11Invoice {
53 Arc::new(invoice.into())
54}
55
56#[cfg(not(feature = "uniffi"))]
57pub fn maybe_convert_invoice(invoice: &Bolt11Invoice) -> &LdkBolt11Invoice {
58 invoice
59}
60#[cfg(feature = "uniffi")]
61pub fn maybe_convert_invoice(invoice: &Bolt11Invoice) -> &LdkBolt11Invoice {
62 &invoice.inner
63}
64
65#[cfg(not(feature = "uniffi"))]
66type Bolt11InvoiceDescription = LdkBolt11InvoiceDescription;
67#[cfg(feature = "uniffi")]
68type Bolt11InvoiceDescription = crate::uniffi_types::Bolt11InvoiceDescription;
69
70macro_rules! maybe_convert_description {
71 ($description: expr) => {{
72 #[cfg(not(feature = "uniffi"))]
73 {
74 $description
75 }
76 #[cfg(feature = "uniffi")]
77 {
78 &LdkBolt11InvoiceDescription::try_from($description)?
79 }
80 }};
81}
82
83pub struct Bolt11Payment {
90 runtime: Arc<RwLock<Option<Arc<tokio::runtime::Runtime>>>>,
91 channel_manager: Arc<ChannelManager>,
92 connection_manager: Arc<ConnectionManager<Arc<Logger>>>,
93 liquidity_source: Option<Arc<LiquiditySource<Arc<Logger>>>>,
94 payment_store: Arc<PaymentStore>,
95 peer_store: Arc<PeerStore<Arc<Logger>>>,
96 config: Arc<Config>,
97 logger: Arc<Logger>,
98}
99
100impl Bolt11Payment {
101 pub(crate) fn new(
102 runtime: Arc<RwLock<Option<Arc<tokio::runtime::Runtime>>>>,
103 channel_manager: Arc<ChannelManager>,
104 connection_manager: Arc<ConnectionManager<Arc<Logger>>>,
105 liquidity_source: Option<Arc<LiquiditySource<Arc<Logger>>>>,
106 payment_store: Arc<PaymentStore>, peer_store: Arc<PeerStore<Arc<Logger>>>,
107 config: Arc<Config>, logger: Arc<Logger>,
108 ) -> Self {
109 Self {
110 runtime,
111 channel_manager,
112 connection_manager,
113 liquidity_source,
114 payment_store,
115 peer_store,
116 config,
117 logger,
118 }
119 }
120
121 pub fn send(
126 &self, invoice: &Bolt11Invoice, sending_parameters: Option<SendingParameters>,
127 ) -> Result<PaymentId, Error> {
128 let invoice = maybe_convert_invoice(invoice);
129 let rt_lock = self.runtime.read().unwrap();
130 if rt_lock.is_none() {
131 return Err(Error::NotRunning);
132 }
133
134 let (payment_hash, recipient_onion, mut route_params) = bolt11_payment::payment_parameters_from_invoice(&invoice).map_err(|_| {
135 log_error!(self.logger, "Failed to send payment due to the given invoice being \"zero-amount\". Please use send_using_amount instead.");
136 Error::InvalidInvoice
137 })?;
138
139 let payment_id = PaymentId(invoice.payment_hash().to_byte_array());
140 if let Some(payment) = self.payment_store.get(&payment_id) {
141 if payment.status == PaymentStatus::Pending
142 || payment.status == PaymentStatus::Succeeded
143 {
144 log_error!(self.logger, "Payment error: an invoice must not be paid twice.");
145 return Err(Error::DuplicatePayment);
146 }
147 }
148
149 let override_params =
150 sending_parameters.as_ref().or(self.config.sending_parameters.as_ref());
151 if let Some(override_params) = override_params {
152 override_params
153 .max_total_routing_fee_msat
154 .map(|f| route_params.max_total_routing_fee_msat = f.into());
155 override_params
156 .max_total_cltv_expiry_delta
157 .map(|d| route_params.payment_params.max_total_cltv_expiry_delta = d);
158 override_params.max_path_count.map(|p| route_params.payment_params.max_path_count = p);
159 override_params
160 .max_channel_saturation_power_of_half
161 .map(|s| route_params.payment_params.max_channel_saturation_power_of_half = s);
162 };
163
164 let payment_secret = Some(*invoice.payment_secret());
165 let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT);
166
167 match self.channel_manager.send_payment(
168 payment_hash,
169 recipient_onion,
170 payment_id,
171 route_params,
172 retry_strategy,
173 ) {
174 Ok(()) => {
175 let payee_pubkey = invoice.recover_payee_pub_key();
176 let amt_msat = invoice.amount_milli_satoshis().unwrap();
177 log_info!(self.logger, "Initiated sending {}msat to {}", amt_msat, payee_pubkey);
178
179 let kind = PaymentKind::Bolt11 {
180 hash: payment_hash,
181 preimage: None,
182 secret: payment_secret,
183 };
184 let payment = PaymentDetails::new(
185 payment_id,
186 kind,
187 invoice.amount_milli_satoshis(),
188 None,
189 PaymentDirection::Outbound,
190 PaymentStatus::Pending,
191 );
192
193 self.payment_store.insert(payment)?;
194
195 Ok(payment_id)
196 },
197 Err(e) => {
198 log_error!(self.logger, "Failed to send payment: {:?}", e);
199 match e {
200 RetryableSendFailure::DuplicatePayment => Err(Error::DuplicatePayment),
201 _ => {
202 let kind = PaymentKind::Bolt11 {
203 hash: payment_hash,
204 preimage: None,
205 secret: payment_secret,
206 };
207 let payment = PaymentDetails::new(
208 payment_id,
209 kind,
210 invoice.amount_milli_satoshis(),
211 None,
212 PaymentDirection::Outbound,
213 PaymentStatus::Failed,
214 );
215
216 self.payment_store.insert(payment)?;
217 Err(Error::PaymentSendingFailed)
218 },
219 }
220 },
221 }
222 }
223
224 pub fn send_using_amount(
234 &self, invoice: &Bolt11Invoice, amount_msat: u64,
235 sending_parameters: Option<SendingParameters>,
236 ) -> Result<PaymentId, Error> {
237 let invoice = maybe_convert_invoice(invoice);
238 let rt_lock = self.runtime.read().unwrap();
239 if rt_lock.is_none() {
240 return Err(Error::NotRunning);
241 }
242
243 if let Some(invoice_amount_msat) = invoice.amount_milli_satoshis() {
244 if amount_msat < invoice_amount_msat {
245 log_error!(
246 self.logger,
247 "Failed to pay as the given amount needs to be at least the invoice amount: required {}msat, gave {}msat.", invoice_amount_msat, amount_msat);
248 return Err(Error::InvalidAmount);
249 }
250 }
251
252 let payment_hash = PaymentHash(invoice.payment_hash().to_byte_array());
253 let payment_id = PaymentId(invoice.payment_hash().to_byte_array());
254 if let Some(payment) = self.payment_store.get(&payment_id) {
255 if payment.status == PaymentStatus::Pending
256 || payment.status == PaymentStatus::Succeeded
257 {
258 log_error!(self.logger, "Payment error: an invoice must not be paid twice.");
259 return Err(Error::DuplicatePayment);
260 }
261 }
262
263 let payment_secret = invoice.payment_secret();
264 let expiry_time = invoice.duration_since_epoch().saturating_add(invoice.expiry_time());
265 let mut payment_params = PaymentParameters::from_node_id(
266 invoice.recover_payee_pub_key(),
267 invoice.min_final_cltv_expiry_delta() as u32,
268 )
269 .with_expiry_time(expiry_time.as_secs())
270 .with_route_hints(invoice.route_hints())
271 .map_err(|_| Error::InvalidInvoice)?;
272 if let Some(features) = invoice.features() {
273 payment_params = payment_params
274 .with_bolt11_features(features.clone())
275 .map_err(|_| Error::InvalidInvoice)?;
276 }
277 let mut route_params =
278 RouteParameters::from_payment_params_and_value(payment_params, amount_msat);
279
280 let override_params =
281 sending_parameters.as_ref().or(self.config.sending_parameters.as_ref());
282 if let Some(override_params) = override_params {
283 override_params
284 .max_total_routing_fee_msat
285 .map(|f| route_params.max_total_routing_fee_msat = f.into());
286 override_params
287 .max_total_cltv_expiry_delta
288 .map(|d| route_params.payment_params.max_total_cltv_expiry_delta = d);
289 override_params.max_path_count.map(|p| route_params.payment_params.max_path_count = p);
290 override_params
291 .max_channel_saturation_power_of_half
292 .map(|s| route_params.payment_params.max_channel_saturation_power_of_half = s);
293 };
294
295 let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT);
296 let recipient_fields = RecipientOnionFields::secret_only(*payment_secret);
297
298 match self.channel_manager.send_payment(
299 payment_hash,
300 recipient_fields,
301 payment_id,
302 route_params,
303 retry_strategy,
304 ) {
305 Ok(()) => {
306 let payee_pubkey = invoice.recover_payee_pub_key();
307 log_info!(
308 self.logger,
309 "Initiated sending {} msat to {}",
310 amount_msat,
311 payee_pubkey
312 );
313
314 let kind = PaymentKind::Bolt11 {
315 hash: payment_hash,
316 preimage: None,
317 secret: Some(*payment_secret),
318 };
319
320 let payment = PaymentDetails::new(
321 payment_id,
322 kind,
323 Some(amount_msat),
324 None,
325 PaymentDirection::Outbound,
326 PaymentStatus::Pending,
327 );
328 self.payment_store.insert(payment)?;
329
330 Ok(payment_id)
331 },
332 Err(e) => {
333 log_error!(self.logger, "Failed to send payment: {:?}", e);
334
335 match e {
336 RetryableSendFailure::DuplicatePayment => Err(Error::DuplicatePayment),
337 _ => {
338 let kind = PaymentKind::Bolt11 {
339 hash: payment_hash,
340 preimage: None,
341 secret: Some(*payment_secret),
342 };
343 let payment = PaymentDetails::new(
344 payment_id,
345 kind,
346 Some(amount_msat),
347 None,
348 PaymentDirection::Outbound,
349 PaymentStatus::Failed,
350 );
351 self.payment_store.insert(payment)?;
352
353 Err(Error::PaymentSendingFailed)
354 },
355 }
356 },
357 }
358 }
359
360 pub fn claim_for_hash(
377 &self, payment_hash: PaymentHash, claimable_amount_msat: u64, preimage: PaymentPreimage,
378 ) -> Result<(), Error> {
379 let payment_id = PaymentId(payment_hash.0);
380
381 let expected_payment_hash = PaymentHash(Sha256::hash(&preimage.0).to_byte_array());
382
383 if expected_payment_hash != payment_hash {
384 log_error!(
385 self.logger,
386 "Failed to manually claim payment as the given preimage doesn't match the hash {}",
387 payment_hash
388 );
389 return Err(Error::InvalidPaymentPreimage);
390 }
391
392 if let Some(details) = self.payment_store.get(&payment_id) {
393 if let Some(expected_amount_msat) = details.amount_msat {
394 if claimable_amount_msat < expected_amount_msat {
395 log_error!(
396 self.logger,
397 "Failed to manually claim payment {} as the claimable amount is less than expected",
398 payment_id
399 );
400 return Err(Error::InvalidAmount);
401 }
402 }
403 } else {
404 log_error!(
405 self.logger,
406 "Failed to manually claim unknown payment with hash: {}",
407 payment_hash
408 );
409 return Err(Error::InvalidPaymentHash);
410 }
411
412 self.channel_manager.claim_funds(preimage);
413 Ok(())
414 }
415
416 pub fn fail_for_hash(&self, payment_hash: PaymentHash) -> Result<(), Error> {
430 let payment_id = PaymentId(payment_hash.0);
431
432 let update = PaymentDetailsUpdate {
433 status: Some(PaymentStatus::Failed),
434 ..PaymentDetailsUpdate::new(payment_id)
435 };
436
437 match self.payment_store.update(&update) {
438 Ok(DataStoreUpdateResult::Updated) | Ok(DataStoreUpdateResult::Unchanged) => (),
439 Ok(DataStoreUpdateResult::NotFound) => {
440 log_error!(
441 self.logger,
442 "Failed to manually fail unknown payment with hash {}",
443 payment_hash,
444 );
445 return Err(Error::InvalidPaymentHash);
446 },
447 Err(e) => {
448 log_error!(
449 self.logger,
450 "Failed to manually fail payment with hash {}: {}",
451 payment_hash,
452 e
453 );
454 return Err(e);
455 },
456 }
457
458 self.channel_manager.fail_htlc_backwards(&payment_hash);
459 Ok(())
460 }
461
462 pub fn receive(
467 &self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32,
468 ) -> Result<Bolt11Invoice, Error> {
469 let description = maybe_convert_description!(description);
470 let invoice = self.receive_inner(Some(amount_msat), description, expiry_secs, None)?;
471 Ok(maybe_wrap_invoice(invoice))
472 }
473
474 pub fn receive_for_hash(
489 &self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32,
490 payment_hash: PaymentHash,
491 ) -> Result<Bolt11Invoice, Error> {
492 let description = maybe_convert_description!(description);
493 let invoice =
494 self.receive_inner(Some(amount_msat), description, expiry_secs, Some(payment_hash))?;
495 Ok(maybe_wrap_invoice(invoice))
496 }
497
498 pub fn receive_variable_amount(
503 &self, description: &Bolt11InvoiceDescription, expiry_secs: u32,
504 ) -> Result<Bolt11Invoice, Error> {
505 let description = maybe_convert_description!(description);
506 let invoice = self.receive_inner(None, description, expiry_secs, None)?;
507 Ok(maybe_wrap_invoice(invoice))
508 }
509
510 pub fn receive_variable_amount_for_hash(
525 &self, description: &Bolt11InvoiceDescription, expiry_secs: u32, payment_hash: PaymentHash,
526 ) -> Result<Bolt11Invoice, Error> {
527 let description = maybe_convert_description!(description);
528 let invoice = self.receive_inner(None, description, expiry_secs, Some(payment_hash))?;
529 Ok(maybe_wrap_invoice(invoice))
530 }
531
532 pub(crate) fn receive_inner(
533 &self, amount_msat: Option<u64>, invoice_description: &LdkBolt11InvoiceDescription,
534 expiry_secs: u32, manual_claim_payment_hash: Option<PaymentHash>,
535 ) -> Result<LdkBolt11Invoice, Error> {
536 let invoice = {
537 let invoice_params = Bolt11InvoiceParameters {
538 amount_msats: amount_msat,
539 description: invoice_description.clone(),
540 invoice_expiry_delta_secs: Some(expiry_secs),
541 payment_hash: manual_claim_payment_hash,
542 ..Default::default()
543 };
544
545 match self.channel_manager.create_bolt11_invoice(invoice_params) {
546 Ok(inv) => {
547 log_info!(self.logger, "Invoice created: {}", inv);
548 inv
549 },
550 Err(e) => {
551 log_error!(self.logger, "Failed to create invoice: {}", e);
552 return Err(Error::InvoiceCreationFailed);
553 },
554 }
555 };
556
557 let payment_hash = PaymentHash(invoice.payment_hash().to_byte_array());
558 let payment_secret = invoice.payment_secret();
559 let id = PaymentId(payment_hash.0);
560 let preimage = if manual_claim_payment_hash.is_none() {
561 let res = self
564 .channel_manager
565 .get_payment_preimage(payment_hash, payment_secret.clone())
566 .ok();
567 debug_assert!(res.is_some(), "We just let ChannelManager create an inbound payment, it can't have forgotten the preimage by now.");
568 res
569 } else {
570 None
571 };
572 let kind = PaymentKind::Bolt11 {
573 hash: payment_hash,
574 preimage,
575 secret: Some(payment_secret.clone()),
576 };
577 let payment = PaymentDetails::new(
578 id,
579 kind,
580 amount_msat,
581 None,
582 PaymentDirection::Inbound,
583 PaymentStatus::Pending,
584 );
585 self.payment_store.insert(payment)?;
586
587 Ok(invoice)
588 }
589
590 pub fn receive_via_jit_channel(
601 &self, amount_msat: u64, description: &Bolt11InvoiceDescription, expiry_secs: u32,
602 max_total_lsp_fee_limit_msat: Option<u64>,
603 ) -> Result<Bolt11Invoice, Error> {
604 let description = maybe_convert_description!(description);
605 let invoice = self.receive_via_jit_channel_inner(
606 Some(amount_msat),
607 description,
608 expiry_secs,
609 max_total_lsp_fee_limit_msat,
610 None,
611 )?;
612 Ok(maybe_wrap_invoice(invoice))
613 }
614
615 pub fn receive_variable_amount_via_jit_channel(
627 &self, description: &Bolt11InvoiceDescription, expiry_secs: u32,
628 max_proportional_lsp_fee_limit_ppm_msat: Option<u64>,
629 ) -> Result<Bolt11Invoice, Error> {
630 let description = maybe_convert_description!(description);
631 let invoice = self.receive_via_jit_channel_inner(
632 None,
633 description,
634 expiry_secs,
635 None,
636 max_proportional_lsp_fee_limit_ppm_msat,
637 )?;
638 Ok(maybe_wrap_invoice(invoice))
639 }
640
641 fn receive_via_jit_channel_inner(
642 &self, amount_msat: Option<u64>, description: &LdkBolt11InvoiceDescription,
643 expiry_secs: u32, max_total_lsp_fee_limit_msat: Option<u64>,
644 max_proportional_lsp_fee_limit_ppm_msat: Option<u64>,
645 ) -> Result<LdkBolt11Invoice, Error> {
646 let liquidity_source =
647 self.liquidity_source.as_ref().ok_or(Error::LiquiditySourceUnavailable)?;
648
649 let (node_id, address) =
650 liquidity_source.get_lsps2_lsp_details().ok_or(Error::LiquiditySourceUnavailable)?;
651
652 let rt_lock = self.runtime.read().unwrap();
653 let runtime = rt_lock.as_ref().unwrap();
654
655 let peer_info = PeerInfo { node_id, address };
656
657 let con_node_id = peer_info.node_id;
658 let con_addr = peer_info.address.clone();
659 let con_cm = Arc::clone(&self.connection_manager);
660
661 tokio::task::block_in_place(move || {
664 runtime.block_on(async move {
665 con_cm.connect_peer_if_necessary(con_node_id, con_addr).await
666 })
667 })?;
668
669 log_info!(self.logger, "Connected to LSP {}@{}. ", peer_info.node_id, peer_info.address);
670
671 let liquidity_source = Arc::clone(&liquidity_source);
672 let (invoice, lsp_total_opening_fee, lsp_prop_opening_fee) =
673 tokio::task::block_in_place(move || {
674 runtime.block_on(async move {
675 if let Some(amount_msat) = amount_msat {
676 liquidity_source
677 .lsps2_receive_to_jit_channel(
678 amount_msat,
679 description,
680 expiry_secs,
681 max_total_lsp_fee_limit_msat,
682 )
683 .await
684 .map(|(invoice, total_fee)| (invoice, Some(total_fee), None))
685 } else {
686 liquidity_source
687 .lsps2_receive_variable_amount_to_jit_channel(
688 description,
689 expiry_secs,
690 max_proportional_lsp_fee_limit_ppm_msat,
691 )
692 .await
693 .map(|(invoice, prop_fee)| (invoice, None, Some(prop_fee)))
694 }
695 })
696 })?;
697
698 let payment_hash = PaymentHash(invoice.payment_hash().to_byte_array());
700 let payment_secret = invoice.payment_secret();
701 let lsp_fee_limits = LSPFeeLimits {
702 max_total_opening_fee_msat: lsp_total_opening_fee,
703 max_proportional_opening_fee_ppm_msat: lsp_prop_opening_fee,
704 };
705 let id = PaymentId(payment_hash.0);
706 let preimage =
707 self.channel_manager.get_payment_preimage(payment_hash, payment_secret.clone()).ok();
708 let kind = PaymentKind::Bolt11Jit {
709 hash: payment_hash,
710 preimage,
711 secret: Some(payment_secret.clone()),
712 counterparty_skimmed_fee_msat: None,
713 lsp_fee_limits,
714 };
715 let payment = PaymentDetails::new(
716 id,
717 kind,
718 amount_msat,
719 None,
720 PaymentDirection::Inbound,
721 PaymentStatus::Pending,
722 );
723 self.payment_store.insert(payment)?;
724
725 self.peer_store.add_peer(peer_info)?;
727
728 Ok(invoice)
729 }
730
731 pub fn send_probes(&self, invoice: &Bolt11Invoice) -> Result<(), Error> {
745 let invoice = maybe_convert_invoice(invoice);
746 let rt_lock = self.runtime.read().unwrap();
747 if rt_lock.is_none() {
748 return Err(Error::NotRunning);
749 }
750
751 let (_payment_hash, _recipient_onion, route_params) = bolt11_payment::payment_parameters_from_invoice(&invoice).map_err(|_| {
752 log_error!(self.logger, "Failed to send probes due to the given invoice being \"zero-amount\". Please use send_probes_using_amount instead.");
753 Error::InvalidInvoice
754 })?;
755
756 let liquidity_limit_multiplier = Some(self.config.probing_liquidity_limit_multiplier);
757
758 self.channel_manager
759 .send_preflight_probes(route_params, liquidity_limit_multiplier)
760 .map_err(|e| {
761 log_error!(self.logger, "Failed to send payment probes: {:?}", e);
762 Error::ProbeSendingFailed
763 })?;
764
765 Ok(())
766 }
767
768 pub fn send_probes_using_amount(
776 &self, invoice: &Bolt11Invoice, amount_msat: u64,
777 ) -> Result<(), Error> {
778 let invoice = maybe_convert_invoice(invoice);
779 let rt_lock = self.runtime.read().unwrap();
780 if rt_lock.is_none() {
781 return Err(Error::NotRunning);
782 }
783
784 let (_payment_hash, _recipient_onion, route_params) = if let Some(invoice_amount_msat) =
785 invoice.amount_milli_satoshis()
786 {
787 if amount_msat < invoice_amount_msat {
788 log_error!(
789 self.logger,
790 "Failed to send probes as the given amount needs to be at least the invoice amount: required {}msat, gave {}msat.", invoice_amount_msat, amount_msat);
791 return Err(Error::InvalidAmount);
792 }
793
794 bolt11_payment::payment_parameters_from_invoice(&invoice).map_err(|_| {
795 log_error!(self.logger, "Failed to send probes due to the given invoice unexpectedly being \"zero-amount\".");
796 Error::InvalidInvoice
797 })?
798 } else {
799 bolt11_payment::payment_parameters_from_variable_amount_invoice(&invoice, amount_msat).map_err(|_| {
800 log_error!(self.logger, "Failed to send probes due to the given invoice unexpectedly being not \"zero-amount\".");
801 Error::InvalidInvoice
802 })?
803 };
804
805 let liquidity_limit_multiplier = Some(self.config.probing_liquidity_limit_multiplier);
806
807 self.channel_manager
808 .send_preflight_probes(route_params, liquidity_limit_multiplier)
809 .map_err(|e| {
810 log_error!(self.logger, "Failed to send payment probes: {:?}", e);
811 Error::ProbeSendingFailed
812 })?;
813
814 Ok(())
815 }
816}