@@ -832,6 +832,13 @@ impl HTLCSource {
832
832
_ => None,
833
833
}
834
834
}
835
+
836
+ pub(crate) fn hold_htlc_at_next_hop(&self) -> bool {
837
+ match self {
838
+ Self::OutboundRoute { hold_htlc, .. } => hold_htlc.is_some(),
839
+ _ => false,
840
+ }
841
+ }
835
842
}
836
843
837
844
/// This enum is used to specify which error data to send to peers when failing back an HTLC
@@ -4920,6 +4927,7 @@ where
4920
4927
invoice_request: None,
4921
4928
bolt12_invoice: None,
4922
4929
session_priv_bytes,
4930
+ hold_htlc_at_next_hop: false,
4923
4931
})
4924
4932
}
4925
4933
@@ -4935,6 +4943,7 @@ where
4935
4943
invoice_request,
4936
4944
bolt12_invoice,
4937
4945
session_priv_bytes,
4946
+ hold_htlc_at_next_hop,
4938
4947
} = args;
4939
4948
// The top-level caller should hold the total_consistency_lock read lock.
4940
4949
debug_assert!(self.total_consistency_lock.try_write().is_err());
@@ -5016,7 +5025,7 @@ where
5016
5025
first_hop_htlc_msat: htlc_msat,
5017
5026
payment_id,
5018
5027
bolt12_invoice: bolt12_invoice.cloned(),
5019
- hold_htlc: None ,
5028
+ hold_htlc: hold_htlc_at_next_hop.then(|| ()) ,
5020
5029
};
5021
5030
let send_res = chan.send_htlc_and_commit(
5022
5031
htlc_msat,
@@ -5402,19 +5411,35 @@ where
5402
5411
},
5403
5412
};
5404
5413
5405
- let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5406
- invoice,
5407
- payment_id,
5408
- self.get_peers_for_blinded_path(),
5409
- );
5410
- if enqueue_held_htlc_available_res.is_err() {
5411
- self.abandon_payment_with_reason(
5414
+ // If the call to `Self::hold_htlc_channels` succeeded, then we are a private node and can
5415
+ // hold the HTLCs for this payment at our next-hop channel counterparty until the recipient
5416
+ // comes online. This allows us to go offline after locking in the HTLCs.
5417
+ if let Ok(channels) = hold_htlc_channels_res {
5418
+ if let Err(e) =
5419
+ self.send_payment_for_static_invoice_no_persist(payment_id, channels)
5420
+ {
5421
+ log_trace!(
5422
+ self.logger,
5423
+ "Failed to send held HTLC with payment id {}: {:?}",
5424
+ payment_id,
5425
+ e
5426
+ );
5427
+ }
5428
+ } else {
5429
+ let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5430
+ invoice,
5412
5431
payment_id,
5413
- PaymentFailureReason::BlindedPathCreationFailed ,
5432
+ self.get_peers_for_blinded_path() ,
5414
5433
);
5415
- res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5416
- return NotifyOption::DoPersist;
5417
- };
5434
+ if enqueue_held_htlc_available_res.is_err() {
5435
+ self.abandon_payment_with_reason(
5436
+ payment_id,
5437
+ PaymentFailureReason::BlindedPathCreationFailed,
5438
+ );
5439
+ res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5440
+ return NotifyOption::DoPersist;
5441
+ };
5442
+ }
5418
5443
5419
5444
NotifyOption::DoPersist
5420
5445
});
@@ -5459,26 +5484,15 @@ where
5459
5484
}
5460
5485
}
5461
5486
5487
+ /// If we want the HTLCs for this payment to be held at the next-hop channel counterparty, use
5488
+ /// [`Self::hold_htlc_channels`] and pass the resulting channels in here.
5462
5489
fn send_payment_for_static_invoice(
5463
- &self, payment_id: PaymentId,
5490
+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
5464
5491
) -> Result<(), Bolt12PaymentError> {
5465
- let best_block_height = self.best_block.read().unwrap().height;
5466
5492
let mut res = Ok(());
5467
5493
PersistenceNotifierGuard::optionally_notify(self, || {
5468
- let outbound_pmts_res = self.pending_outbound_payments.send_payment_for_static_invoice(
5469
- payment_id,
5470
- &self.router,
5471
- self.list_usable_channels(),
5472
- || self.compute_inflight_htlcs(),
5473
- &self.entropy_source,
5474
- &self.node_signer,
5475
- &self,
5476
- &self.secp_ctx,
5477
- best_block_height,
5478
- &self.logger,
5479
- &self.pending_events,
5480
- |args| self.send_payment_along_path(args),
5481
- );
5494
+ let outbound_pmts_res =
5495
+ self.send_payment_for_static_invoice_no_persist(payment_id, first_hops);
5482
5496
match outbound_pmts_res {
5483
5497
Err(Bolt12PaymentError::UnexpectedInvoice)
5484
5498
| Err(Bolt12PaymentError::DuplicateInvoice) => {
@@ -5494,6 +5508,27 @@ where
5494
5508
res
5495
5509
}
5496
5510
5511
+ /// Useful if the caller is already triggering a persist of the `ChannelManager`.
5512
+ fn send_payment_for_static_invoice_no_persist(
5513
+ &self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
5514
+ ) -> Result<(), Bolt12PaymentError> {
5515
+ let best_block_height = self.best_block.read().unwrap().height;
5516
+ self.pending_outbound_payments.send_payment_for_static_invoice(
5517
+ payment_id,
5518
+ &self.router,
5519
+ first_hops,
5520
+ || self.compute_inflight_htlcs(),
5521
+ &self.entropy_source,
5522
+ &self.node_signer,
5523
+ &self,
5524
+ &self.secp_ctx,
5525
+ best_block_height,
5526
+ &self.logger,
5527
+ &self.pending_events,
5528
+ |args| self.send_payment_along_path(args),
5529
+ )
5530
+ }
5531
+
5497
5532
/// If we are holding an HTLC on behalf of an often-offline sender, this method allows us to
5498
5533
/// create a path for the sender to use as the reply path when they send the recipient a
5499
5534
/// [`HeldHtlcAvailable`] onion message, so the recipient's [`ReleaseHeldHtlc`] response will be
@@ -14742,7 +14777,9 @@ where
14742
14777
fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, context: AsyncPaymentsContext) {
14743
14778
match context {
14744
14779
AsyncPaymentsContext::OutboundPayment { payment_id } => {
14745
- if let Err(e) = self.send_payment_for_static_invoice(payment_id) {
14780
+ if let Err(e) =
14781
+ self.send_payment_for_static_invoice(payment_id, self.list_usable_channels())
14782
+ {
14746
14783
log_trace!(
14747
14784
self.logger,
14748
14785
"Failed to release held HTLC with payment id {}: {:?}",
0 commit comments