@@ -229,6 +229,9 @@ pub enum PendingHTLCRouting {
229
229
blinded: Option<BlindedForward>,
230
230
/// The absolute CLTV of the inbound HTLC
231
231
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<()>,
232
235
},
233
236
/// An HTLC which should be forwarded on to another Trampoline node.
234
237
TrampolineForward {
@@ -371,6 +374,15 @@ impl PendingHTLCRouting {
371
374
Self::ReceiveKeysend { incoming_cltv_expiry, .. } => Some(*incoming_cltv_expiry),
372
375
}
373
376
}
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
+ }
374
386
}
375
387
376
388
/// Information about an incoming HTLC, including the [`PendingHTLCRouting`] describing where it
@@ -641,6 +653,34 @@ impl Readable for PaymentId {
641
653
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
642
654
pub struct InterceptId(pub [u8; 32]);
643
655
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, channel_id: &ChannelId, counterparty_node_id: &PublicKey,
667
+ ) -> Self {
668
+ let htlc_id_size = core::mem::size_of::<u64>();
669
+ let chan_id_size = core::mem::size_of::<ChannelId>();
670
+ let cp_id_serialized = counterparty_node_id.serialize();
671
+
672
+ const RES_SIZE: usize = 8 + 32 + 33;
673
+ debug_assert_eq!(RES_SIZE, htlc_id_size + chan_id_size + cp_id_serialized.len());
674
+
675
+ let mut res = [0u8; RES_SIZE];
676
+ res[..htlc_id_size].copy_from_slice(&htlc_id.to_be_bytes());
677
+ res[htlc_id_size..htlc_id_size + chan_id_size].copy_from_slice(&channel_id.0);
678
+ res[htlc_id_size + chan_id_size..].copy_from_slice(&cp_id_serialized);
679
+
680
+ Self(Sha256::hash(&res[..]).to_byte_array())
681
+ }
682
+ }
683
+
644
684
impl Writeable for InterceptId {
645
685
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
646
686
self.0.write(w)
@@ -2588,8 +2628,14 @@ pub struct ChannelManager<
2588
2628
pub(super) forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
2589
2629
#[cfg(not(test))]
2590
2630
forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
2591
- /// Storage for HTLCs that have been intercepted and bubbled up to the user. We hold them here
2592
- /// until the user tells us what we should do with them.
2631
+ /// Storage for HTLCs that have been intercepted.
2632
+ ///
2633
+ /// These HTLCs fall into two categories:
2634
+ /// 1. HTLCs that are bubbled up to the user and held until the invocation of
2635
+ /// [`ChannelManager::forward_intercepted_htlc`] or [`ChannelManager::fail_intercepted_htlc`]
2636
+ /// (or timeout)
2637
+ /// 2. HTLCs that are being held on behalf of an often-offline sender until receipt of a
2638
+ /// [`ReleaseHeldHtlc`] onion message from an often-offline recipient
2593
2639
///
2594
2640
/// See `ChannelManager` struct-level documentation for lock order requirements.
2595
2641
pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
@@ -6268,13 +6314,18 @@ where
6268
6314
})?;
6269
6315
6270
6316
let routing = match payment.forward_info.routing {
6271
- PendingHTLCRouting::Forward { onion_packet, blinded, incoming_cltv_expiry, .. } => {
6272
- PendingHTLCRouting::Forward {
6273
- onion_packet,
6274
- blinded,
6275
- incoming_cltv_expiry,
6276
- short_channel_id: next_hop_scid,
6277
- }
6317
+ PendingHTLCRouting::Forward {
6318
+ onion_packet,
6319
+ blinded,
6320
+ incoming_cltv_expiry,
6321
+ hold_htlc,
6322
+ ..
6323
+ } => PendingHTLCRouting::Forward {
6324
+ onion_packet,
6325
+ blinded,
6326
+ incoming_cltv_expiry,
6327
+ hold_htlc,
6328
+ short_channel_id: next_hop_scid,
6278
6329
},
6279
6330
_ => unreachable!(), // Only `PendingHTLCRouting::Forward`s are intercepted
6280
6331
};
@@ -10675,7 +10726,25 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
10675
10726
};
10676
10727
match forward_htlcs.entry(scid) {
10677
10728
hash_map::Entry::Occupied(mut entry) => {
10678
- entry.get_mut().push(HTLCForwardInfo::AddHTLC(pending_add));
10729
+ if pending_add.forward_info.routing.should_hold_htlc() {
10730
+ let intercept_id = InterceptId::from_htlc_id_and_chan_id(
10731
+ prev_htlc_id,
10732
+ &prev_channel_id,
10733
+ &prev_counterparty_node_id,
10734
+ );
10735
+ let mut held_htlcs = self.pending_intercepted_htlcs.lock().unwrap();
10736
+ match held_htlcs.entry(intercept_id) {
10737
+ hash_map::Entry::Vacant(entry) => {
10738
+ entry.insert(pending_add);
10739
+ },
10740
+ hash_map::Entry::Occupied(_) => {
10741
+ debug_assert!(false, "Should never have two HTLCs with the same scid and htlc id");
10742
+ fail_intercepted_htlc();
10743
+ },
10744
+ }
10745
+ } else {
10746
+ entry.get_mut().push(HTLCForwardInfo::AddHTLC(pending_add));
10747
+ }
10679
10748
},
10680
10749
hash_map::Entry::Vacant(entry) => {
10681
10750
if !is_our_scid
@@ -10685,9 +10754,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
10685
10754
scid,
10686
10755
&self.chain_hash,
10687
10756
) {
10688
- let intercept_id = InterceptId(
10689
- Sha256::hash(&pending_add.forward_info.incoming_shared_secret)
10690
- .to_byte_array(),
10757
+ let intercept_id = InterceptId::from_incoming_shared_secret(
10758
+ &pending_add.forward_info.incoming_shared_secret,
10691
10759
);
10692
10760
let mut pending_intercepts =
10693
10761
self.pending_intercepted_htlcs.lock().unwrap();
@@ -10725,6 +10793,22 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
10725
10793
fail_intercepted_htlc();
10726
10794
},
10727
10795
}
10796
+ } else if pending_add.forward_info.routing.should_hold_htlc() {
10797
+ let intercept_id = InterceptId::from_htlc_id_and_chan_id(
10798
+ prev_htlc_id,
10799
+ &prev_channel_id,
10800
+ &prev_counterparty_node_id,
10801
+ );
10802
+ let mut held_htlcs = self.pending_intercepted_htlcs.lock().unwrap();
10803
+ match held_htlcs.entry(intercept_id) {
10804
+ hash_map::Entry::Vacant(entry) => {
10805
+ entry.insert(pending_add);
10806
+ },
10807
+ hash_map::Entry::Occupied(_) => {
10808
+ debug_assert!(false, "Should never have two HTLCs with the same scid and htlc id");
10809
+ fail_intercepted_htlc();
10810
+ },
10811
+ }
10728
10812
} else {
10729
10813
entry.insert(vec![HTLCForwardInfo::AddHTLC(pending_add)]);
10730
10814
}
@@ -14836,6 +14920,7 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
14836
14920
(1, blinded, option),
14837
14921
(2, short_channel_id, required),
14838
14922
(3, incoming_cltv_expiry, option),
14923
+ (5, hold_htlc, option),
14839
14924
},
14840
14925
(1, Receive) => {
14841
14926
(0, payment_data, required),
0 commit comments