@@ -842,6 +842,13 @@ impl HTLCSource {
842842 _ => None,
843843 }
844844 }
845+
846+ pub(crate) fn hold_htlc_at_next_hop(&self) -> bool {
847+ match self {
848+ Self::OutboundRoute { hold_htlc, .. } => hold_htlc.is_some(),
849+ _ => false,
850+ }
851+ }
845852}
846853
847854/// This enum is used to specify which error data to send to peers when failing back an HTLC
@@ -4944,6 +4951,7 @@ where
49444951 invoice_request: None,
49454952 bolt12_invoice: None,
49464953 session_priv_bytes,
4954+ hold_htlc_at_next_hop: false,
49474955 })
49484956 }
49494957
@@ -4959,6 +4967,7 @@ where
49594967 invoice_request,
49604968 bolt12_invoice,
49614969 session_priv_bytes,
4970+ hold_htlc_at_next_hop,
49624971 } = args;
49634972 // The top-level caller should hold the total_consistency_lock read lock.
49644973 debug_assert!(self.total_consistency_lock.try_write().is_err());
@@ -5040,7 +5049,7 @@ where
50405049 first_hop_htlc_msat: htlc_msat,
50415050 payment_id,
50425051 bolt12_invoice: bolt12_invoice.cloned(),
5043- hold_htlc: None ,
5052+ hold_htlc: hold_htlc_at_next_hop.then(|| ()) ,
50445053 };
50455054 let send_res = chan.send_htlc_and_commit(
50465055 htlc_msat,
@@ -5426,19 +5435,35 @@ where
54265435 },
54275436 };
54285437
5429- let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5430- invoice,
5431- payment_id,
5432- self.get_peers_for_blinded_path(),
5433- );
5434- if enqueue_held_htlc_available_res.is_err() {
5435- self.abandon_payment_with_reason(
5438+ // If the call to `Self::hold_htlc_channels` succeeded, then we are a private node and can
5439+ // hold the HTLCs for this payment at our next-hop channel counterparty until the recipient
5440+ // comes online. This allows us to go offline after locking in the HTLCs.
5441+ if let Ok(channels) = hold_htlc_channels_res {
5442+ if let Err(e) =
5443+ self.send_payment_for_static_invoice_no_persist(payment_id, channels)
5444+ {
5445+ log_trace!(
5446+ self.logger,
5447+ "Failed to send held HTLC with payment id {}: {:?}",
5448+ payment_id,
5449+ e
5450+ );
5451+ }
5452+ } else {
5453+ let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5454+ invoice,
54365455 payment_id,
5437- PaymentFailureReason::BlindedPathCreationFailed ,
5456+ self.get_peers_for_blinded_path() ,
54385457 );
5439- res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5440- return NotifyOption::DoPersist;
5441- };
5458+ if enqueue_held_htlc_available_res.is_err() {
5459+ self.abandon_payment_with_reason(
5460+ payment_id,
5461+ PaymentFailureReason::BlindedPathCreationFailed,
5462+ );
5463+ res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5464+ return NotifyOption::DoPersist;
5465+ };
5466+ }
54425467
54435468 NotifyOption::DoPersist
54445469 });
@@ -5483,26 +5508,15 @@ where
54835508 }
54845509 }
54855510
5511+ /// If we want the HTLCs for this payment to be held at the next-hop channel counterparty, use
5512+ /// [`Self::hold_htlc_channels`] and pass the resulting channels in here.
54865513 fn send_payment_for_static_invoice(
5487- &self, payment_id: PaymentId,
5514+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
54885515 ) -> Result<(), Bolt12PaymentError> {
5489- let best_block_height = self.best_block.read().unwrap().height;
54905516 let mut res = Ok(());
54915517 PersistenceNotifierGuard::optionally_notify(self, || {
5492- let outbound_pmts_res = self.pending_outbound_payments.send_payment_for_static_invoice(
5493- payment_id,
5494- &self.router,
5495- self.list_usable_channels(),
5496- || self.compute_inflight_htlcs(),
5497- &self.entropy_source,
5498- &self.node_signer,
5499- &self,
5500- &self.secp_ctx,
5501- best_block_height,
5502- &self.logger,
5503- &self.pending_events,
5504- |args| self.send_payment_along_path(args),
5505- );
5518+ let outbound_pmts_res =
5519+ self.send_payment_for_static_invoice_no_persist(payment_id, first_hops);
55065520 match outbound_pmts_res {
55075521 Err(Bolt12PaymentError::UnexpectedInvoice)
55085522 | Err(Bolt12PaymentError::DuplicateInvoice) => {
@@ -5518,6 +5532,27 @@ where
55185532 res
55195533 }
55205534
5535+ /// Useful if the caller is already triggering a persist of the `ChannelManager`.
5536+ fn send_payment_for_static_invoice_no_persist(
5537+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
5538+ ) -> Result<(), Bolt12PaymentError> {
5539+ let best_block_height = self.best_block.read().unwrap().height;
5540+ self.pending_outbound_payments.send_payment_for_static_invoice(
5541+ payment_id,
5542+ &self.router,
5543+ first_hops,
5544+ || self.compute_inflight_htlcs(),
5545+ &self.entropy_source,
5546+ &self.node_signer,
5547+ &self,
5548+ &self.secp_ctx,
5549+ best_block_height,
5550+ &self.logger,
5551+ &self.pending_events,
5552+ |args| self.send_payment_along_path(args),
5553+ )
5554+ }
5555+
55215556 /// If we are holding an HTLC on behalf of an often-offline sender, this method allows us to
55225557 /// create a path for the sender to use as the reply path when they send the recipient a
55235558 /// [`HeldHtlcAvailable`] onion message, so the recipient's [`ReleaseHeldHtlc`] response will be
@@ -14845,7 +14880,9 @@ where
1484514880 fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, context: AsyncPaymentsContext) {
1484614881 match context {
1484714882 AsyncPaymentsContext::OutboundPayment { payment_id } => {
14848- if let Err(e) = self.send_payment_for_static_invoice(payment_id) {
14883+ if let Err(e) =
14884+ self.send_payment_for_static_invoice(payment_id, self.list_usable_channels())
14885+ {
1484914886 log_trace!(
1485014887 self.logger,
1485114888 "Failed to release held HTLC with payment id {}: {:?}",
0 commit comments