Skip to content

Commit 6264eec

Browse files
Store held htlcs in pending_intercepted_htlcs
As part of supporting sending payments as an often-offline sender, the sender's always-online channel counterparty needs to hold onto the sender's HTLC until they receive a release_held_htlc onion message from the often-offline recipient. Here we implement storing these held HTLCs in the existing ChannelManager::pending_intercepted_htlcs map. We want to move in the direction of obviating the need to persistence the ChannelManager entirely, so it doesn't really make sense to add a whole new map for these HTLCs.
1 parent aabf685 commit 6264eec

File tree

2 files changed

+99
-13
lines changed

2 files changed

+99
-13
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 98 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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
@@ -641,6 +653,34 @@ impl Readable for PaymentId {
641653
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
642654
pub 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, 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+
644684
impl Writeable for InterceptId {
645685
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
646686
self.0.write(w)
@@ -2588,8 +2628,14 @@ pub struct ChannelManager<
25882628
pub(super) forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
25892629
#[cfg(not(test))]
25902630
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
25932639
///
25942640
/// See `ChannelManager` struct-level documentation for lock order requirements.
25952641
pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
@@ -6268,13 +6314,18 @@ where
62686314
})?;
62696315

62706316
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,
62786329
},
62796330
_ => unreachable!(), // Only `PendingHTLCRouting::Forward`s are intercepted
62806331
};
@@ -10675,7 +10726,25 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1067510726
};
1067610727
match forward_htlcs.entry(scid) {
1067710728
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+
}
1067910748
},
1068010749
hash_map::Entry::Vacant(entry) => {
1068110750
if !is_our_scid
@@ -10685,9 +10754,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1068510754
scid,
1068610755
&self.chain_hash,
1068710756
) {
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,
1069110759
);
1069210760
let mut pending_intercepts =
1069310761
self.pending_intercepted_htlcs.lock().unwrap();
@@ -10725,6 +10793,22 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
1072510793
fail_intercepted_htlc();
1072610794
},
1072710795
}
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+
}
1072810812
} else {
1072910813
entry.insert(vec![HTLCForwardInfo::AddHTLC(pending_add)]);
1073010814
}
@@ -14836,6 +14920,7 @@ impl_writeable_tlv_based_enum!(PendingHTLCRouting,
1483614920
(1, blinded, option),
1483714921
(2, short_channel_id, required),
1483814922
(3, incoming_cltv_expiry, option),
14923+
(5, hold_htlc, option),
1483914924
},
1484014925
(1, Receive) => {
1484114926
(0, payment_data, required),

lightning/src/ln/onion_payment.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ pub(super) fn create_fwd_pending_htlc_info(
190190
onion_packet: outgoing_packet,
191191
short_channel_id,
192192
incoming_cltv_expiry: Some(msg.cltv_expiry),
193+
hold_htlc: msg.hold_htlc,
193194
blinded: intro_node_blinding_point.or(msg.blinding_point)
194195
.map(|bp| BlindedForward {
195196
inbound_blinding_point: bp,

0 commit comments

Comments
 (0)