@@ -853,6 +853,13 @@ impl HTLCSource {
853853 _ => None,
854854 }
855855 }
856+
857+ pub(crate) fn hold_htlc_at_next_hop(&self) -> bool {
858+ match self {
859+ Self::OutboundRoute { hold_htlc, .. } => hold_htlc.is_some(),
860+ _ => false,
861+ }
862+ }
856863}
857864
858865/// This enum is used to specify which error data to send to peers when failing back an HTLC
@@ -4955,6 +4962,7 @@ where
49554962 invoice_request: None,
49564963 bolt12_invoice: None,
49574964 session_priv_bytes,
4965+ hold_htlc_at_next_hop: false,
49584966 })
49594967 }
49604968
@@ -4970,6 +4978,7 @@ where
49704978 invoice_request,
49714979 bolt12_invoice,
49724980 session_priv_bytes,
4981+ hold_htlc_at_next_hop,
49734982 } = args;
49744983 // The top-level caller should hold the total_consistency_lock read lock.
49754984 debug_assert!(self.total_consistency_lock.try_write().is_err());
@@ -5051,7 +5060,7 @@ where
50515060 first_hop_htlc_msat: htlc_msat,
50525061 payment_id,
50535062 bolt12_invoice: bolt12_invoice.cloned(),
5054- hold_htlc: None ,
5063+ hold_htlc: hold_htlc_at_next_hop.then(|| ()) ,
50555064 };
50565065 let send_res = chan.send_htlc_and_commit(
50575066 htlc_msat,
@@ -5437,19 +5446,35 @@ where
54375446 },
54385447 };
54395448
5440- let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5441- invoice,
5442- payment_id,
5443- self.get_peers_for_blinded_path(),
5444- );
5445- if enqueue_held_htlc_available_res.is_err() {
5446- self.abandon_payment_with_reason(
5449+ // If the call to `Self::hold_htlc_channels` succeeded, then we are a private node and can
5450+ // hold the HTLCs for this payment at our next-hop channel counterparty until the recipient
5451+ // comes online. This allows us to go offline after locking in the HTLCs.
5452+ if let Ok(channels) = hold_htlc_channels_res {
5453+ if let Err(e) =
5454+ self.send_payment_for_static_invoice_no_persist(payment_id, channels)
5455+ {
5456+ log_trace!(
5457+ self.logger,
5458+ "Failed to send held HTLC with payment id {}: {:?}",
5459+ payment_id,
5460+ e
5461+ );
5462+ }
5463+ } else {
5464+ let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5465+ invoice,
54475466 payment_id,
5448- PaymentFailureReason::BlindedPathCreationFailed ,
5467+ self.get_peers_for_blinded_path() ,
54495468 );
5450- res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5451- return NotifyOption::DoPersist;
5452- };
5469+ if enqueue_held_htlc_available_res.is_err() {
5470+ self.abandon_payment_with_reason(
5471+ payment_id,
5472+ PaymentFailureReason::BlindedPathCreationFailed,
5473+ );
5474+ res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5475+ return NotifyOption::DoPersist;
5476+ };
5477+ }
54535478
54545479 NotifyOption::DoPersist
54555480 });
@@ -5494,26 +5519,15 @@ where
54945519 }
54955520 }
54965521
5522+ /// If we want the HTLCs for this payment to be held at the next-hop channel counterparty, use
5523+ /// [`Self::hold_htlc_channels`] and pass the resulting channels in here.
54975524 fn send_payment_for_static_invoice(
5498- &self, payment_id: PaymentId,
5525+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
54995526 ) -> Result<(), Bolt12PaymentError> {
5500- let best_block_height = self.best_block.read().unwrap().height;
55015527 let mut res = Ok(());
55025528 PersistenceNotifierGuard::optionally_notify(self, || {
5503- let outbound_pmts_res = self.pending_outbound_payments.send_payment_for_static_invoice(
5504- payment_id,
5505- &self.router,
5506- self.list_usable_channels(),
5507- || self.compute_inflight_htlcs(),
5508- &self.entropy_source,
5509- &self.node_signer,
5510- &self,
5511- &self.secp_ctx,
5512- best_block_height,
5513- &self.logger,
5514- &self.pending_events,
5515- |args| self.send_payment_along_path(args),
5516- );
5529+ let outbound_pmts_res =
5530+ self.send_payment_for_static_invoice_no_persist(payment_id, first_hops);
55175531 match outbound_pmts_res {
55185532 Err(Bolt12PaymentError::UnexpectedInvoice)
55195533 | Err(Bolt12PaymentError::DuplicateInvoice) => {
@@ -5529,6 +5543,27 @@ where
55295543 res
55305544 }
55315545
5546+ /// Useful if the caller is already triggering a persist of the `ChannelManager`.
5547+ fn send_payment_for_static_invoice_no_persist(
5548+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
5549+ ) -> Result<(), Bolt12PaymentError> {
5550+ let best_block_height = self.best_block.read().unwrap().height;
5551+ self.pending_outbound_payments.send_payment_for_static_invoice(
5552+ payment_id,
5553+ &self.router,
5554+ first_hops,
5555+ || self.compute_inflight_htlcs(),
5556+ &self.entropy_source,
5557+ &self.node_signer,
5558+ &self,
5559+ &self.secp_ctx,
5560+ best_block_height,
5561+ &self.logger,
5562+ &self.pending_events,
5563+ |args| self.send_payment_along_path(args),
5564+ )
5565+ }
5566+
55325567 /// If we are holding an HTLC on behalf of an often-offline sender, this method allows us to
55335568 /// create a path for the sender to use as the reply path when they send the recipient a
55345569 /// [`HeldHtlcAvailable`] onion message, so the recipient's [`ReleaseHeldHtlc`] response will be
@@ -14861,7 +14896,9 @@ where
1486114896 fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, context: AsyncPaymentsContext) {
1486214897 match context {
1486314898 AsyncPaymentsContext::OutboundPayment { payment_id } => {
14864- if let Err(e) = self.send_payment_for_static_invoice(payment_id) {
14899+ if let Err(e) =
14900+ self.send_payment_for_static_invoice(payment_id, self.list_usable_channels())
14901+ {
1486514902 log_trace!(
1486614903 self.logger,
1486714904 "Failed to release held HTLC with payment id {}: {:?}",
0 commit comments