@@ -235,6 +235,7 @@ pub enum PendingHTLCRouting {
235235 blinded: Option<BlindedForward>,
236236 /// The absolute CLTV of the inbound HTLC
237237 incoming_cltv_expiry: Option<u32>,
238+ /// Signals that the HTLC should be held for release by an offline recipient.
238239 hold_htlc: bool,
239240 },
240241 /// An HTLC which should be forwarded on to another Trampoline node.
@@ -2558,6 +2559,7 @@ pub struct ChannelManager<
25582559 /// See `ChannelManager` struct-level documentation for lock order requirements.
25592560 pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
25602561
2562+ #[cfg(async_payments)]
25612563 pending_held_htlcs: Mutex<HashMap<(u64, u64), PendingAddHTLCInfo>>,
25622564
25632565 /// SCID/SCID Alias -> pending `update_add_htlc`s to decode.
@@ -3757,6 +3759,8 @@ where
37573759 decode_update_add_htlcs: Mutex::new(new_hash_map()),
37583760 claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: new_hash_map(), pending_claiming_payments: new_hash_map() }),
37593761 pending_intercepted_htlcs: Mutex::new(new_hash_map()),
3762+ #[cfg(async_payments)]
3763+ pending_held_htlcs: Mutex::new(new_hash_map()),
37603764 short_to_chan_info: FairRwLock::new(new_hash_map()),
37613765
37623766 our_network_pubkey,
@@ -6052,6 +6056,7 @@ where
60526056 Ok(())
60536057 }
60546058
6059+ #[cfg(async_payments)]
60556060 fn forward_held_htlc(&self, short_channel_id: u64, htlc_id: u64) -> Result<(), APIError> {
60566061 let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
60576062
@@ -10314,11 +10319,44 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1031410319 let mut failed_intercept_forwards = Vec::new();
1031510320 if !pending_forwards.is_empty() {
1031610321 for (forward_info, prev_htlc_id) in pending_forwards.drain(..) {
10322+ // If this HTLC needs to be held for release by an offline recipient, we'll move it to a dedicated
10323+ // hash map for held HTLCs.
10324+ #[cfg(async_payments)]
1031710325 if let PendingHTLCRouting::Forward { hold_htlc: true, .. } =
1031810326 forward_info.routing
1031910327 {
1032010328 let mut held_htlcs = self.pending_held_htlcs.lock().unwrap();
10321- held_htlcs.entry((prev_short_channel_id, prev_htlc_id));
10329+ held_htlcs.insert(
10330+ (prev_short_channel_id, prev_htlc_id),
10331+ PendingAddHTLCInfo {
10332+ prev_short_channel_id,
10333+ prev_counterparty_node_id,
10334+ prev_funding_outpoint,
10335+ prev_channel_id,
10336+ prev_htlc_id,
10337+ prev_user_channel_id,
10338+ forward_info,
10339+ },
10340+ );
10341+
10342+ // TODO: Where to get message paths from?!?
10343+ let message_paths = [];
10344+
10345+ self.flow.enqueue_held_forward_htlc_available(
10346+ prev_short_channel_id,
10347+ prev_htlc_id,
10348+ self.get_peers_for_blinded_path(),
10349+ &message_paths,
10350+ );
10351+
10352+ log_debug!(
10353+ self.logger,
10354+ "Holding HTLC {}:{} for release by an offline recipient",
10355+ prev_short_channel_id,
10356+ prev_htlc_id
10357+ );
10358+
10359+ continue;
1032210360 }
1032310361
1032410362 let scid = match forward_info.routing {
@@ -14387,19 +14425,30 @@ where
1438714425 fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {
1438814426 #[cfg(async_payments)]
1438914427 {
14390- let payment_id = match _context {
14391- AsyncPaymentsContext::OutboundPayment { payment_id } => payment_id,
14428+ match _context {
14429+ AsyncPaymentsContext::OutboundPayment { payment_id } => {
14430+ if let Err(e) = self.send_payment_for_static_invoice(payment_id) {
14431+ log_trace!(
14432+ self.logger,
14433+ "Failed to release held HTLC with payment id {}: {:?}",
14434+ payment_id,
14435+ e
14436+ );
14437+ }
14438+ },
14439+ AsyncPaymentsContext::OutboundHTLC { chan_id, htlc_id } => {
14440+ if let Err(e) = self.forward_held_htlc(chan_id, htlc_id) {
14441+ log_trace!(
14442+ self.logger,
14443+ "Failed to release held forward HTLC {}:{} {:?}",
14444+ chan_id,
14445+ htlc_id,
14446+ e
14447+ );
14448+ }
14449+ },
1439214450 _ => return,
1439314451 };
14394-
14395- if let Err(e) = self.send_payment_for_static_invoice(payment_id) {
14396- log_trace!(
14397- self.logger,
14398- "Failed to release held HTLC with payment id {}: {:?}",
14399- payment_id,
14400- e
14401- );
14402- }
1440314452 }
1440414453 }
1440514454
@@ -16772,6 +16821,8 @@ where
1677216821 inbound_payment_key: expanded_inbound_key,
1677316822 pending_outbound_payments: pending_outbounds,
1677416823 pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),
16824+ #[cfg(async_payments)]
16825+ pending_held_htlcs: Mutex::new(new_hash_map()), // TODO: Persistence.
1677516826
1677616827 forward_htlcs: Mutex::new(forward_htlcs),
1677716828 decode_update_add_htlcs: Mutex::new(decode_update_add_htlcs),
0 commit comments