@@ -852,6 +852,13 @@ impl HTLCSource {
852852 _ => None,
853853 }
854854 }
855+
856+ pub(crate) fn hold_htlc_at_next_hop(&self) -> bool {
857+ match self {
858+ Self::OutboundRoute { hold_htlc, .. } => hold_htlc.is_some(),
859+ _ => false,
860+ }
861+ }
855862}
856863
857864/// This enum is used to specify which error data to send to peers when failing back an HTLC
@@ -4954,6 +4961,7 @@ where
49544961 invoice_request: None,
49554962 bolt12_invoice: None,
49564963 session_priv_bytes,
4964+ hold_htlc_at_next_hop: false,
49574965 })
49584966 }
49594967
@@ -4969,6 +4977,7 @@ where
49694977 invoice_request,
49704978 bolt12_invoice,
49714979 session_priv_bytes,
4980+ hold_htlc_at_next_hop,
49724981 } = args;
49734982 // The top-level caller should hold the total_consistency_lock read lock.
49744983 debug_assert!(self.total_consistency_lock.try_write().is_err());
@@ -5050,7 +5059,7 @@ where
50505059 first_hop_htlc_msat: htlc_msat,
50515060 payment_id,
50525061 bolt12_invoice: bolt12_invoice.cloned(),
5053- hold_htlc: None ,
5062+ hold_htlc: hold_htlc_at_next_hop.then(|| ()) ,
50545063 };
50555064 let send_res = chan.send_htlc_and_commit(
50565065 htlc_msat,
@@ -5436,19 +5445,35 @@ where
54365445 },
54375446 };
54385447
5439- let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5440- invoice,
5441- payment_id,
5442- self.get_peers_for_blinded_path(),
5443- );
5444- if enqueue_held_htlc_available_res.is_err() {
5445- self.abandon_payment_with_reason(
5448+ // If the call to `Self::hold_htlc_channels` succeeded, then we are a private node and can
5449+ // hold the HTLCs for this payment at our next-hop channel counterparty until the recipient
5450+ // comes online. This allows us to go offline after locking in the HTLCs.
5451+ if let Ok(channels) = hold_htlc_channels_res {
5452+ if let Err(e) =
5453+ self.send_payment_for_static_invoice_no_persist(payment_id, channels)
5454+ {
5455+ log_trace!(
5456+ self.logger,
5457+ "Failed to send held HTLC with payment id {}: {:?}",
5458+ payment_id,
5459+ e
5460+ );
5461+ }
5462+ } else {
5463+ let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5464+ invoice,
54465465 payment_id,
5447- PaymentFailureReason::BlindedPathCreationFailed ,
5466+ self.get_peers_for_blinded_path() ,
54485467 );
5449- res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5450- return NotifyOption::DoPersist;
5451- };
5468+ if enqueue_held_htlc_available_res.is_err() {
5469+ self.abandon_payment_with_reason(
5470+ payment_id,
5471+ PaymentFailureReason::BlindedPathCreationFailed,
5472+ );
5473+ res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5474+ return NotifyOption::DoPersist;
5475+ };
5476+ }
54525477
54535478 NotifyOption::DoPersist
54545479 });
@@ -5493,26 +5518,15 @@ where
54935518 }
54945519 }
54955520
5521+ /// If we want the HTLCs for this payment to be held at the next-hop channel counterparty, use
5522+ /// [`Self::hold_htlc_channels`] and pass the resulting channels in here.
54965523 fn send_payment_for_static_invoice(
5497- &self, payment_id: PaymentId,
5524+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
54985525 ) -> Result<(), Bolt12PaymentError> {
5499- let best_block_height = self.best_block.read().unwrap().height;
55005526 let mut res = Ok(());
55015527 PersistenceNotifierGuard::optionally_notify(self, || {
5502- let outbound_pmts_res = self.pending_outbound_payments.send_payment_for_static_invoice(
5503- payment_id,
5504- &self.router,
5505- self.list_usable_channels(),
5506- || self.compute_inflight_htlcs(),
5507- &self.entropy_source,
5508- &self.node_signer,
5509- &self,
5510- &self.secp_ctx,
5511- best_block_height,
5512- &self.logger,
5513- &self.pending_events,
5514- |args| self.send_payment_along_path(args),
5515- );
5528+ let outbound_pmts_res =
5529+ self.send_payment_for_static_invoice_no_persist(payment_id, first_hops);
55165530 match outbound_pmts_res {
55175531 Err(Bolt12PaymentError::UnexpectedInvoice)
55185532 | Err(Bolt12PaymentError::DuplicateInvoice) => {
@@ -5528,6 +5542,27 @@ where
55285542 res
55295543 }
55305544
5545+ /// Useful if the caller is already triggering a persist of the `ChannelManager`.
5546+ fn send_payment_for_static_invoice_no_persist(
5547+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
5548+ ) -> Result<(), Bolt12PaymentError> {
5549+ let best_block_height = self.best_block.read().unwrap().height;
5550+ self.pending_outbound_payments.send_payment_for_static_invoice(
5551+ payment_id,
5552+ &self.router,
5553+ first_hops,
5554+ || self.compute_inflight_htlcs(),
5555+ &self.entropy_source,
5556+ &self.node_signer,
5557+ &self,
5558+ &self.secp_ctx,
5559+ best_block_height,
5560+ &self.logger,
5561+ &self.pending_events,
5562+ |args| self.send_payment_along_path(args),
5563+ )
5564+ }
5565+
55315566 /// If we are holding an HTLC on behalf of an often-offline sender, this method allows us to
55325567 /// create a path for the sender to use as the reply path when they send the recipient a
55335568 /// [`HeldHtlcAvailable`] onion message, so the recipient's [`ReleaseHeldHtlc`] response will be
@@ -14851,7 +14886,9 @@ where
1485114886 fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, context: AsyncPaymentsContext) {
1485214887 match context {
1485314888 AsyncPaymentsContext::OutboundPayment { payment_id } => {
14854- if let Err(e) = self.send_payment_for_static_invoice(payment_id) {
14889+ if let Err(e) =
14890+ self.send_payment_for_static_invoice(payment_id, self.list_usable_channels())
14891+ {
1485514892 log_trace!(
1485614893 self.logger,
1485714894 "Failed to release held HTLC with payment id {}: {:?}",
0 commit comments