@@ -852,6 +852,13 @@ impl HTLCSource {
852
852
_ => None,
853
853
}
854
854
}
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
+ }
855
862
}
856
863
857
864
/// This enum is used to specify which error data to send to peers when failing back an HTLC
@@ -4954,6 +4961,7 @@ where
4954
4961
invoice_request: None,
4955
4962
bolt12_invoice: None,
4956
4963
session_priv_bytes,
4964
+ hold_htlc_at_next_hop: false,
4957
4965
})
4958
4966
}
4959
4967
@@ -4969,6 +4977,7 @@ where
4969
4977
invoice_request,
4970
4978
bolt12_invoice,
4971
4979
session_priv_bytes,
4980
+ hold_htlc_at_next_hop,
4972
4981
} = args;
4973
4982
// The top-level caller should hold the total_consistency_lock read lock.
4974
4983
debug_assert!(self.total_consistency_lock.try_write().is_err());
@@ -5050,7 +5059,7 @@ where
5050
5059
first_hop_htlc_msat: htlc_msat,
5051
5060
payment_id,
5052
5061
bolt12_invoice: bolt12_invoice.cloned(),
5053
- hold_htlc: None ,
5062
+ hold_htlc: hold_htlc_at_next_hop.then(|| ()) ,
5054
5063
};
5055
5064
let send_res = chan.send_htlc_and_commit(
5056
5065
htlc_msat,
@@ -5436,19 +5445,35 @@ where
5436
5445
},
5437
5446
};
5438
5447
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,
5446
5465
payment_id,
5447
- PaymentFailureReason::BlindedPathCreationFailed ,
5466
+ self.get_peers_for_blinded_path() ,
5448
5467
);
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
+ }
5452
5477
5453
5478
NotifyOption::DoPersist
5454
5479
});
@@ -5493,26 +5518,15 @@ where
5493
5518
}
5494
5519
}
5495
5520
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.
5496
5523
fn send_payment_for_static_invoice(
5497
- &self, payment_id: PaymentId,
5524
+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
5498
5525
) -> Result<(), Bolt12PaymentError> {
5499
- let best_block_height = self.best_block.read().unwrap().height;
5500
5526
let mut res = Ok(());
5501
5527
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);
5516
5530
match outbound_pmts_res {
5517
5531
Err(Bolt12PaymentError::UnexpectedInvoice)
5518
5532
| Err(Bolt12PaymentError::DuplicateInvoice) => {
@@ -5528,6 +5542,27 @@ where
5528
5542
res
5529
5543
}
5530
5544
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
+
5531
5566
/// If we are holding an HTLC on behalf of an often-offline sender, this method allows us to
5532
5567
/// create a path for the sender to use as the reply path when they send the recipient a
5533
5568
/// [`HeldHtlcAvailable`] onion message, so the recipient's [`ReleaseHeldHtlc`] response will be
@@ -14851,7 +14886,9 @@ where
14851
14886
fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, context: AsyncPaymentsContext) {
14852
14887
match context {
14853
14888
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
+ {
14855
14892
log_trace!(
14856
14893
self.logger,
14857
14894
"Failed to release held HTLC with payment id {}: {:?}",
0 commit comments