@@ -229,6 +229,9 @@ pub enum PendingHTLCRouting {
229229 blinded: Option<BlindedForward>,
230230 /// The absolute CLTV of the inbound HTLC
231231 incoming_cltv_expiry: Option<u32>,
232+ /// Whether this HTLC should be held by our node until we receive a corresponding
233+ /// [`ReleaseHeldHtlc`] onion message.
234+ hold_htlc: Option<()>,
232235 },
233236 /// An HTLC which should be forwarded on to another Trampoline node.
234237 TrampolineForward {
@@ -371,6 +374,15 @@ impl PendingHTLCRouting {
371374 Self::ReceiveKeysend { incoming_cltv_expiry, .. } => Some(*incoming_cltv_expiry),
372375 }
373376 }
377+
378+ /// Whether this HTLC should be held by our node until we receive a corresponding
379+ /// [`ReleaseHeldHtlc`] onion message.
380+ fn should_hold_htlc(&self) -> bool {
381+ match self {
382+ Self::Forward { hold_htlc: Some(()), .. } => true,
383+ _ => false,
384+ }
385+ }
374386}
375387
376388/// Information about an incoming HTLC, including the [`PendingHTLCRouting`] describing where it
@@ -638,9 +650,47 @@ impl Readable for PaymentId {
638650/// An identifier used to uniquely identify an intercepted HTLC to LDK.
639651///
640652/// This is not exported to bindings users as we just use [u8; 32] directly
641- #[derive(Hash, Copy, Clone, PartialEq, Eq, Debug )]
653+ #[derive(Hash, Copy, Clone, PartialEq, Eq)]
642654pub struct InterceptId(pub [u8; 32]);
643655
656+ impl InterceptId {
657+ /// This intercept id corresponds to an HTLC that will be forwarded on
658+ /// [`ChannelManager::forward_intercepted_htlc`].
659+ fn from_incoming_shared_secret(ss: &[u8; 32]) -> Self {
660+ Self(Sha256::hash(ss).to_byte_array())
661+ }
662+
663+ /// This intercept id corresponds to an HTLC that will be forwarded on receipt of a
664+ /// [`ReleaseHeldHtlc`] onion message.
665+ fn from_htlc_id_and_chan_id(
666+ htlc_id: u64, chan_id: &ChannelId, counterparty_node_id: &PublicKey,
667+ ) -> Self {
668+ let htlc_id_bytes = htlc_id.to_be_bytes();
669+ let cp_id_bytes = counterparty_node_id.serialize();
670+
671+ const RES_SIZE: usize = 8 + 32 + 33;
672+ debug_assert_eq!(RES_SIZE, htlc_id_bytes.len() + chan_id.0.len() + cp_id_bytes.len());
673+
674+ let mut res = [0u8; RES_SIZE];
675+ res[..htlc_id_bytes.len()].copy_from_slice(&htlc_id_bytes);
676+ res[htlc_id_bytes.len()..htlc_id_bytes.len() + chan_id.0.len()].copy_from_slice(&chan_id.0);
677+ res[htlc_id_bytes.len() + chan_id.0.len()..].copy_from_slice(&cp_id_bytes);
678+
679+ Self(Sha256::hash(&res[..]).to_byte_array())
680+ }
681+ }
682+
683+ impl Borrow<[u8]> for InterceptId {
684+ fn borrow(&self) -> &[u8] {
685+ &self.0[..]
686+ }
687+ }
688+ impl_fmt_traits! {
689+ impl fmt_traits for InterceptId {
690+ const LENGTH: usize = 32;
691+ }
692+ }
693+
644694impl Writeable for InterceptId {
645695 fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
646696 self.0.write(w)
@@ -2598,8 +2648,14 @@ pub struct ChannelManager<
25982648 pub(super) forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
25992649 #[cfg(not(test))]
26002650 forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
2601- /// Storage for HTLCs that have been intercepted and bubbled up to the user. We hold them here
2602- /// until the user tells us what we should do with them.
2651+ /// Storage for HTLCs that have been intercepted.
2652+ ///
2653+ /// These HTLCs fall into two categories:
2654+ /// 1. HTLCs that are bubbled up to the user and held until the invocation of
2655+ /// [`ChannelManager::forward_intercepted_htlc`] or [`ChannelManager::fail_intercepted_htlc`]
2656+ /// (or timeout)
2657+ /// 2. HTLCs that are being held on behalf of an often-offline sender until receipt of a
2658+ /// [`ReleaseHeldHtlc`] onion message from an often-offline recipient
26032659 ///
26042660 /// See `ChannelManager` struct-level documentation for lock order requirements.
26052661 pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
@@ -6282,11 +6338,19 @@ where
62826338 })?;
62836339
62846340 let routing = match payment.forward_info.routing {
6285- PendingHTLCRouting::Forward { onion_packet, blinded, incoming_cltv_expiry, .. } => {
6341+ PendingHTLCRouting::Forward {
6342+ onion_packet,
6343+ blinded,
6344+ incoming_cltv_expiry,
6345+ hold_htlc,
6346+ ..
6347+ } => {
6348+ debug_assert!(hold_htlc.is_none(), "Held intercept HTLCs should not be surfaced in an event until the recipient comes online");
62866349 PendingHTLCRouting::Forward {
62876350 onion_packet,
62886351 blinded,
62896352 incoming_cltv_expiry,
6353+ hold_htlc,
62906354 short_channel_id: next_hop_scid,
62916355 }
62926356 },
@@ -10719,16 +10783,43 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1071910783 ));
1072010784 };
1072110785
10722- if !is_our_scid
10786+ // In the case that we have an HTLC that we're supposed to hold onto until the
10787+ // recipient comes online *and* the outbound scid is encoded as
10788+ // `fake_scid::is_valid_intercept`, we should first wait for the recipient to come
10789+ // online before generating an `HTLCIntercepted` event, since the event cannot be
10790+ // acted on until the recipient is online to cooperatively open the JIT channel. Once
10791+ // we receive the `ReleaseHeldHtlc` message from the recipient, we will circle back
10792+ // here and resume generating the event below.
10793+ if pending_add.forward_info.routing.should_hold_htlc() {
10794+ let intercept_id = InterceptId::from_htlc_id_and_chan_id(
10795+ prev_htlc_id,
10796+ &prev_channel_id,
10797+ &prev_counterparty_node_id,
10798+ );
10799+ let mut held_htlcs = self.pending_intercepted_htlcs.lock().unwrap();
10800+ match held_htlcs.entry(intercept_id) {
10801+ hash_map::Entry::Vacant(entry) => {
10802+ log_trace!(
10803+ logger,
10804+ "Intercepted held HTLC with id {}, holding until the recipient is online",
10805+ intercept_id
10806+ );
10807+ entry.insert(pending_add);
10808+ },
10809+ hash_map::Entry::Occupied(_) => {
10810+ debug_assert!(false, "Should never have two HTLCs with the same channel id and htlc id");
10811+ fail_intercepted_htlc(pending_add);
10812+ },
10813+ }
10814+ } else if !is_our_scid
1072310815 && pending_add.forward_info.incoming_amt_msat.is_some()
1072410816 && fake_scid::is_valid_intercept(
1072510817 &self.fake_scid_rand_bytes,
1072610818 scid,
1072710819 &self.chain_hash,
1072810820 ) {
10729- let intercept_id = InterceptId(
10730- Sha256::hash(&pending_add.forward_info.incoming_shared_secret)
10731- .to_byte_array(),
10821+ let intercept_id = InterceptId::from_incoming_shared_secret(
10822+ &pending_add.forward_info.incoming_shared_secret,
1073210823 );
1073310824 let mut pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
1073410825 match pending_intercepts.entry(intercept_id) {
@@ -14905,6 +14996,7 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
1490514996 (1, blinded, option),
1490614997 (2, short_channel_id, required),
1490714998 (3, incoming_cltv_expiry, option),
14999+ (4, hold_htlc, option),
1490815000 },
1490915001 (1, Receive) => {
1491015002 (0, payment_data, required),
0 commit comments