@@ -844,6 +844,13 @@ impl HTLCSource {
844844 _ => None,
845845 }
846846 }
847+
848+ pub(crate) fn hold_htlc_at_next_hop(&self) -> bool {
849+ match self {
850+ Self::OutboundRoute { hold_htlc, .. } => hold_htlc.is_some(),
851+ _ => false,
852+ }
853+ }
847854}
848855
849856/// This enum is used to specify which error data to send to peers when failing back an HTLC
@@ -4943,6 +4950,7 @@ where
49434950 invoice_request: None,
49444951 bolt12_invoice: None,
49454952 session_priv_bytes,
4953+ hold_htlc_at_next_hop: false,
49464954 })
49474955 }
49484956
@@ -4958,6 +4966,7 @@ where
49584966 invoice_request,
49594967 bolt12_invoice,
49604968 session_priv_bytes,
4969+ hold_htlc_at_next_hop,
49614970 } = args;
49624971 // The top-level caller should hold the total_consistency_lock read lock.
49634972 debug_assert!(self.total_consistency_lock.try_write().is_err());
@@ -5039,7 +5048,7 @@ where
50395048 first_hop_htlc_msat: htlc_msat,
50405049 payment_id,
50415050 bolt12_invoice: bolt12_invoice.cloned(),
5042- hold_htlc: None ,
5051+ hold_htlc: hold_htlc_at_next_hop.then(|| ()) ,
50435052 };
50445053 let send_res = chan.send_htlc_and_commit(
50455054 htlc_msat,
@@ -5425,19 +5434,35 @@ where
54255434 },
54265435 };
54275436
5428- let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5429- invoice,
5430- payment_id,
5431- self.get_peers_for_blinded_path(),
5432- );
5433- if enqueue_held_htlc_available_res.is_err() {
5434- self.abandon_payment_with_reason(
5437+ // If the call to `Self::hold_htlc_channels` succeeded, then we are a private node and can
5438+ // hold the HTLCs for this payment at our next-hop channel counterparty until the recipient
5439+ // comes online. This allows us to go offline after locking in the HTLCs.
5440+ if let Ok(channels) = hold_htlc_channels_res {
5441+ if let Err(e) =
5442+ self.send_payment_for_static_invoice_no_persist(payment_id, channels)
5443+ {
5444+ log_trace!(
5445+ self.logger,
5446+ "Failed to send held HTLC with payment id {}: {:?}",
5447+ payment_id,
5448+ e
5449+ );
5450+ }
5451+ } else {
5452+ let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5453+ invoice,
54355454 payment_id,
5436- PaymentFailureReason::BlindedPathCreationFailed ,
5455+ self.get_peers_for_blinded_path() ,
54375456 );
5438- res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5439- return NotifyOption::DoPersist;
5440- };
5457+ if enqueue_held_htlc_available_res.is_err() {
5458+ self.abandon_payment_with_reason(
5459+ payment_id,
5460+ PaymentFailureReason::BlindedPathCreationFailed,
5461+ );
5462+ res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5463+ return NotifyOption::DoPersist;
5464+ };
5465+ }
54415466
54425467 NotifyOption::DoPersist
54435468 });
@@ -5482,26 +5507,15 @@ where
54825507 }
54835508 }
54845509
5510+ /// If we want the HTLCs for this payment to be held at the next-hop channel counterparty, use
5511+ /// [`Self::hold_htlc_channels`] and pass the resulting channels in here.
54855512 fn send_payment_for_static_invoice(
5486- &self, payment_id: PaymentId,
5513+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
54875514 ) -> Result<(), Bolt12PaymentError> {
5488- let best_block_height = self.best_block.read().unwrap().height;
54895515 let mut res = Ok(());
54905516 PersistenceNotifierGuard::optionally_notify(self, || {
5491- let outbound_pmts_res = self.pending_outbound_payments.send_payment_for_static_invoice(
5492- payment_id,
5493- &self.router,
5494- self.list_usable_channels(),
5495- || self.compute_inflight_htlcs(),
5496- &self.entropy_source,
5497- &self.node_signer,
5498- &self,
5499- &self.secp_ctx,
5500- best_block_height,
5501- &self.logger,
5502- &self.pending_events,
5503- |args| self.send_payment_along_path(args),
5504- );
5517+ let outbound_pmts_res =
5518+ self.send_payment_for_static_invoice_no_persist(payment_id, first_hops);
55055519 match outbound_pmts_res {
55065520 Err(Bolt12PaymentError::UnexpectedInvoice)
55075521 | Err(Bolt12PaymentError::DuplicateInvoice) => {
@@ -5517,6 +5531,27 @@ where
55175531 res
55185532 }
55195533
5534+ /// Useful if the caller is already triggering a persist of the `ChannelManager`.
5535+ fn send_payment_for_static_invoice_no_persist(
5536+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
5537+ ) -> Result<(), Bolt12PaymentError> {
5538+ let best_block_height = self.best_block.read().unwrap().height;
5539+ self.pending_outbound_payments.send_payment_for_static_invoice(
5540+ payment_id,
5541+ &self.router,
5542+ first_hops,
5543+ || self.compute_inflight_htlcs(),
5544+ &self.entropy_source,
5545+ &self.node_signer,
5546+ &self,
5547+ &self.secp_ctx,
5548+ best_block_height,
5549+ &self.logger,
5550+ &self.pending_events,
5551+ |args| self.send_payment_along_path(args),
5552+ )
5553+ }
5554+
55205555 /// If we are holding an HTLC on behalf of an often-offline sender, this method allows us to
55215556 /// create a path for the sender to use as the reply path when they send the recipient a
55225557 /// [`HeldHtlcAvailable`] onion message, so the recipient's [`ReleaseHeldHtlc`] response will be
@@ -14734,7 +14769,9 @@ where
1473414769 fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, context: AsyncPaymentsContext) {
1473514770 match context {
1473614771 AsyncPaymentsContext::OutboundPayment { payment_id } => {
14737- if let Err(e) = self.send_payment_for_static_invoice(payment_id) {
14772+ if let Err(e) =
14773+ self.send_payment_for_static_invoice(payment_id, self.list_usable_channels())
14774+ {
1473814775 log_trace!(
1473914776 self.logger,
1474014777 "Failed to release held HTLC with payment id {}: {:?}",
0 commit comments