@@ -845,6 +845,13 @@ impl HTLCSource {
845845 _ => None,
846846 }
847847 }
848+
849+ pub(crate) fn hold_htlc_at_next_hop(&self) -> bool {
850+ match self {
851+ Self::OutboundRoute { hold_htlc, .. } => hold_htlc.is_some(),
852+ _ => false,
853+ }
854+ }
848855}
849856
850857/// This enum is used to specify which error data to send to peers when failing back an HTLC
@@ -4947,6 +4954,7 @@ where
49474954 invoice_request: None,
49484955 bolt12_invoice: None,
49494956 session_priv_bytes,
4957+ hold_htlc_at_next_hop: false,
49504958 })
49514959 }
49524960
@@ -4962,6 +4970,7 @@ where
49624970 invoice_request,
49634971 bolt12_invoice,
49644972 session_priv_bytes,
4973+ hold_htlc_at_next_hop,
49654974 } = args;
49664975 // The top-level caller should hold the total_consistency_lock read lock.
49674976 debug_assert!(self.total_consistency_lock.try_write().is_err());
@@ -5043,7 +5052,7 @@ where
50435052 first_hop_htlc_msat: htlc_msat,
50445053 payment_id,
50455054 bolt12_invoice: bolt12_invoice.cloned(),
5046- hold_htlc: None ,
5055+ hold_htlc: hold_htlc_at_next_hop.then(|| ()) ,
50475056 };
50485057 let send_res = chan.send_htlc_and_commit(
50495058 htlc_msat,
@@ -5429,19 +5438,35 @@ where
54295438 },
54305439 };
54315440
5432- let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5433- invoice,
5434- payment_id,
5435- self.get_peers_for_blinded_path(),
5436- );
5437- if enqueue_held_htlc_available_res.is_err() {
5438- self.abandon_payment_with_reason(
5441+ // If the call to `Self::hold_htlc_channels` succeeded, then we are a private node and can
5442+ // hold the HTLCs for this payment at our next-hop channel counterparty until the recipient
5443+ // comes online. This allows us to go offline after locking in the HTLCs.
5444+ if let Ok(channels) = hold_htlc_channels_res {
5445+ if let Err(e) =
5446+ self.send_payment_for_static_invoice_no_persist(payment_id, channels)
5447+ {
5448+ log_trace!(
5449+ self.logger,
5450+ "Failed to send held HTLC with payment id {}: {:?}",
5451+ payment_id,
5452+ e
5453+ );
5454+ }
5455+ } else {
5456+ let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5457+ invoice,
54395458 payment_id,
5440- PaymentFailureReason::BlindedPathCreationFailed ,
5459+ self.get_peers_for_blinded_path() ,
54415460 );
5442- res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5443- return NotifyOption::DoPersist;
5444- };
5461+ if enqueue_held_htlc_available_res.is_err() {
5462+ self.abandon_payment_with_reason(
5463+ payment_id,
5464+ PaymentFailureReason::BlindedPathCreationFailed,
5465+ );
5466+ res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5467+ return NotifyOption::DoPersist;
5468+ };
5469+ }
54455470
54465471 NotifyOption::DoPersist
54475472 });
@@ -5486,26 +5511,15 @@ where
54865511 }
54875512 }
54885513
5514+ /// If we want the HTLCs for this payment to be held at the next-hop channel counterparty, use
5515+ /// [`Self::hold_htlc_channels`] and pass the resulting channels in here.
54895516 fn send_payment_for_static_invoice(
5490- &self, payment_id: PaymentId,
5517+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
54915518 ) -> Result<(), Bolt12PaymentError> {
5492- let best_block_height = self.best_block.read().unwrap().height;
54935519 let mut res = Ok(());
54945520 PersistenceNotifierGuard::optionally_notify(self, || {
5495- let outbound_pmts_res = self.pending_outbound_payments.send_payment_for_static_invoice(
5496- payment_id,
5497- &self.router,
5498- self.list_usable_channels(),
5499- || self.compute_inflight_htlcs(),
5500- &self.entropy_source,
5501- &self.node_signer,
5502- &self,
5503- &self.secp_ctx,
5504- best_block_height,
5505- &self.logger,
5506- &self.pending_events,
5507- |args| self.send_payment_along_path(args),
5508- );
5521+ let outbound_pmts_res =
5522+ self.send_payment_for_static_invoice_no_persist(payment_id, first_hops);
55095523 match outbound_pmts_res {
55105524 Err(Bolt12PaymentError::UnexpectedInvoice)
55115525 | Err(Bolt12PaymentError::DuplicateInvoice) => {
@@ -5521,6 +5535,27 @@ where
55215535 res
55225536 }
55235537
5538+ /// Useful if the caller is already triggering a persist of the `ChannelManager`.
5539+ fn send_payment_for_static_invoice_no_persist(
5540+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
5541+ ) -> Result<(), Bolt12PaymentError> {
5542+ let best_block_height = self.best_block.read().unwrap().height;
5543+ self.pending_outbound_payments.send_payment_for_static_invoice(
5544+ payment_id,
5545+ &self.router,
5546+ first_hops,
5547+ || self.compute_inflight_htlcs(),
5548+ &self.entropy_source,
5549+ &self.node_signer,
5550+ &self,
5551+ &self.secp_ctx,
5552+ best_block_height,
5553+ &self.logger,
5554+ &self.pending_events,
5555+ |args| self.send_payment_along_path(args),
5556+ )
5557+ }
5558+
55245559 /// If we are holding an HTLC on behalf of an often-offline sender, this method allows us to
55255560 /// create a path for the sender to use as the reply path when they send the recipient a
55265561 /// [`HeldHtlcAvailable`] onion message, so the recipient's [`ReleaseHeldHtlc`] response will be
@@ -14844,7 +14879,9 @@ where
1484414879 fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, context: AsyncPaymentsContext) {
1484514880 match context {
1484614881 AsyncPaymentsContext::OutboundPayment { payment_id } => {
14847- if let Err(e) = self.send_payment_for_static_invoice(payment_id) {
14882+ if let Err(e) =
14883+ self.send_payment_for_static_invoice(payment_id, self.list_usable_channels())
14884+ {
1484814885 log_trace!(
1484914886 self.logger,
1485014887 "Failed to release held HTLC with payment id {}: {:?}",
0 commit comments