Skip to content

Commit fb6e018

Browse files
authored
Merge pull request #1835 from valentinewallace/2022-11-jit-chan-htlc-intercept
Intercept HTLC forwards for JIT channels
2 parents 2f0ddf0 + acff8f6 commit fb6e018

File tree

5 files changed

+575
-21
lines changed

5 files changed

+575
-21
lines changed

lightning/src/ln/channelmanager.rs

+244-16
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ use core::ops::Deref;
9292
pub(super) enum PendingHTLCRouting {
9393
Forward {
9494
onion_packet: msgs::OnionPacket,
95-
/// The SCID from the onion that we should forward to. This could be a "real" SCID, an
96-
/// outbound SCID alias, or a phantom node SCID.
95+
/// The SCID from the onion that we should forward to. This could be a real SCID or a fake one
96+
/// generated using `get_fake_scid` from the scid_utils::fake_scid module.
9797
short_channel_id: u64, // This should be NonZero<u64> eventually when we bump MSRV
9898
},
9999
Receive {
@@ -207,6 +207,24 @@ impl Readable for PaymentId {
207207
Ok(PaymentId(buf))
208208
}
209209
}
210+
211+
/// An identifier used to uniquely identify an intercepted HTLC to LDK.
212+
/// (C-not exported) as we just use [u8; 32] directly
213+
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
214+
pub struct InterceptId(pub [u8; 32]);
215+
216+
impl Writeable for InterceptId {
217+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
218+
self.0.write(w)
219+
}
220+
}
221+
222+
impl Readable for InterceptId {
223+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
224+
let buf: [u8; 32] = Readable::read(r)?;
225+
Ok(InterceptId(buf))
226+
}
227+
}
210228
/// Tracks the inbound corresponding to an outbound HTLC
211229
#[allow(clippy::derive_hash_xor_eq)] // Our Hash is faithful to the data, we just don't have SecretKey::hash
212230
#[derive(Clone, PartialEq, Eq)]
@@ -666,6 +684,8 @@ pub type SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, M, T, F, L> = ChannelManage
666684
// `total_consistency_lock`
667685
// |
668686
// |__`forward_htlcs`
687+
// | |
688+
// | |__`pending_intercepted_htlcs`
669689
// |
670690
// |__`pending_inbound_payments`
671691
// | |
@@ -751,6 +771,11 @@ pub struct ChannelManager<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
751771
pub(super) forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
752772
#[cfg(not(test))]
753773
forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
774+
/// Storage for HTLCs that have been intercepted and bubbled up to the user. We hold them here
775+
/// until the user tells us what we should do with them.
776+
///
777+
/// See `ChannelManager` struct-level documentation for lock order requirements.
778+
pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
754779

755780
/// Map from payment hash to the payment data and any HTLCs which are to us and can be
756781
/// failed/claimed by the user.
@@ -1566,6 +1591,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
15661591
pending_outbound_payments: Mutex::new(HashMap::new()),
15671592
forward_htlcs: Mutex::new(HashMap::new()),
15681593
claimable_htlcs: Mutex::new(HashMap::new()),
1594+
pending_intercepted_htlcs: Mutex::new(HashMap::new()),
15691595
id_to_peer: Mutex::new(HashMap::new()),
15701596
short_to_chan_info: FairRwLock::new(HashMap::new()),
15711597

@@ -2206,8 +2232,11 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
22062232
let forwarding_id_opt = match id_option {
22072233
None => { // unknown_next_peer
22082234
// Note that this is likely a timing oracle for detecting whether an scid is a
2209-
// phantom.
2210-
if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) {
2235+
// phantom or an intercept.
2236+
if (self.default_configuration.accept_intercept_htlcs &&
2237+
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)) ||
2238+
fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)
2239+
{
22112240
None
22122241
} else {
22132242
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
@@ -3023,6 +3052,102 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
30233052
Ok(())
30243053
}
30253054

3055+
/// Attempts to forward an intercepted HTLC over the provided channel id and with the provided
3056+
/// amount to forward. Should only be called in response to an [`HTLCIntercepted`] event.
3057+
///
3058+
/// Intercepted HTLCs can be useful for Lightning Service Providers (LSPs) to open a just-in-time
3059+
/// channel to a receiving node if the node lacks sufficient inbound liquidity.
3060+
///
3061+
/// To make use of intercepted HTLCs, set [`UserConfig::accept_intercept_htlcs`] and use
3062+
/// [`ChannelManager::get_intercept_scid`] to generate short channel id(s) to put in the
3063+
/// receiver's invoice route hints. These route hints will signal to LDK to generate an
3064+
/// [`HTLCIntercepted`] event when it receives the forwarded HTLC, and this method or
3065+
/// [`ChannelManager::fail_intercepted_htlc`] MUST be called in response to the event.
3066+
///
3067+
/// Note that LDK does not enforce fee requirements in `amt_to_forward_msat`, and will not stop
3068+
/// you from forwarding more than you received.
3069+
///
3070+
/// Errors if the event was not handled in time, in which case the HTLC was automatically failed
3071+
/// backwards.
3072+
///
3073+
/// [`UserConfig::accept_intercept_htlcs`]: crate::util::config::UserConfig::accept_intercept_htlcs
3074+
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
3075+
// TODO: when we move to deciding the best outbound channel at forward time, only take
3076+
// `next_node_id` and not `next_hop_channel_id`
3077+
pub fn forward_intercepted_htlc(&self, intercept_id: InterceptId, next_hop_channel_id: &[u8; 32], _next_node_id: PublicKey, amt_to_forward_msat: u64) -> Result<(), APIError> {
3078+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
3079+
3080+
let next_hop_scid = match self.channel_state.lock().unwrap().by_id.get(next_hop_channel_id) {
3081+
Some(chan) => {
3082+
if !chan.is_usable() {
3083+
return Err(APIError::APIMisuseError {
3084+
err: format!("Channel with id {:?} not fully established", next_hop_channel_id)
3085+
})
3086+
}
3087+
chan.get_short_channel_id().unwrap_or(chan.outbound_scid_alias())
3088+
},
3089+
None => return Err(APIError::APIMisuseError {
3090+
err: format!("Channel with id {:?} not found", next_hop_channel_id)
3091+
})
3092+
};
3093+
3094+
let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
3095+
.ok_or_else(|| APIError::APIMisuseError {
3096+
err: format!("Payment with intercept id {:?} not found", intercept_id.0)
3097+
})?;
3098+
3099+
let routing = match payment.forward_info.routing {
3100+
PendingHTLCRouting::Forward { onion_packet, .. } => {
3101+
PendingHTLCRouting::Forward { onion_packet, short_channel_id: next_hop_scid }
3102+
},
3103+
_ => unreachable!() // Only `PendingHTLCRouting::Forward`s are intercepted
3104+
};
3105+
let pending_htlc_info = PendingHTLCInfo {
3106+
outgoing_amt_msat: amt_to_forward_msat, routing, ..payment.forward_info
3107+
};
3108+
3109+
let mut per_source_pending_forward = [(
3110+
payment.prev_short_channel_id,
3111+
payment.prev_funding_outpoint,
3112+
payment.prev_user_channel_id,
3113+
vec![(pending_htlc_info, payment.prev_htlc_id)]
3114+
)];
3115+
self.forward_htlcs(&mut per_source_pending_forward);
3116+
Ok(())
3117+
}
3118+
3119+
/// Fails the intercepted HTLC indicated by intercept_id. Should only be called in response to
3120+
/// an [`HTLCIntercepted`] event. See [`ChannelManager::forward_intercepted_htlc`].
3121+
///
3122+
/// Errors if the event was not handled in time, in which case the HTLC was automatically failed
3123+
/// backwards.
3124+
///
3125+
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
3126+
pub fn fail_intercepted_htlc(&self, intercept_id: InterceptId) -> Result<(), APIError> {
3127+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
3128+
3129+
let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
3130+
.ok_or_else(|| APIError::APIMisuseError {
3131+
err: format!("Payment with InterceptId {:?} not found", intercept_id)
3132+
})?;
3133+
3134+
if let PendingHTLCRouting::Forward { short_channel_id, .. } = payment.forward_info.routing {
3135+
let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
3136+
short_channel_id: payment.prev_short_channel_id,
3137+
outpoint: payment.prev_funding_outpoint,
3138+
htlc_id: payment.prev_htlc_id,
3139+
incoming_packet_shared_secret: payment.forward_info.incoming_shared_secret,
3140+
phantom_shared_secret: None,
3141+
});
3142+
3143+
let failure_reason = HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() };
3144+
let destination = HTLCDestination::UnknownNextHop { requested_forward_scid: short_channel_id };
3145+
self.fail_htlc_backwards_internal(htlc_source, &payment.forward_info.payment_hash, failure_reason, destination);
3146+
} else { unreachable!() } // Only `PendingHTLCRouting::Forward`s are intercepted
3147+
3148+
Ok(())
3149+
}
3150+
30263151
/// Processes HTLCs which are pending waiting on random forward delay.
30273152
///
30283153
/// Should only really ever be called in response to a PendingHTLCsForwardable event.
@@ -5067,28 +5192,82 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
50675192
fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) {
50685193
for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
50695194
let mut forward_event = None;
5195+
let mut new_intercept_events = Vec::new();
5196+
let mut failed_intercept_forwards = Vec::new();
50705197
if !pending_forwards.is_empty() {
5071-
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
5072-
if forward_htlcs.is_empty() {
5073-
forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS))
5074-
}
50755198
for (forward_info, prev_htlc_id) in pending_forwards.drain(..) {
5076-
match forward_htlcs.entry(match forward_info.routing {
5077-
PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
5078-
PendingHTLCRouting::Receive { .. } => 0,
5079-
PendingHTLCRouting::ReceiveKeysend { .. } => 0,
5080-
}) {
5199+
let scid = match forward_info.routing {
5200+
PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
5201+
PendingHTLCRouting::Receive { .. } => 0,
5202+
PendingHTLCRouting::ReceiveKeysend { .. } => 0,
5203+
};
5204+
// Pull this now to avoid introducing a lock order with `forward_htlcs`.
5205+
let is_our_scid = self.short_to_chan_info.read().unwrap().contains_key(&scid);
5206+
5207+
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
5208+
let forward_htlcs_empty = forward_htlcs.is_empty();
5209+
match forward_htlcs.entry(scid) {
50815210
hash_map::Entry::Occupied(mut entry) => {
50825211
entry.get_mut().push(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
50835212
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }));
50845213
},
50855214
hash_map::Entry::Vacant(entry) => {
5086-
entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
5087-
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
5215+
if !is_our_scid && forward_info.incoming_amt_msat.is_some() &&
5216+
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, scid, &self.genesis_hash)
5217+
{
5218+
let intercept_id = InterceptId(Sha256::hash(&forward_info.incoming_shared_secret).into_inner());
5219+
let mut pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
5220+
match pending_intercepts.entry(intercept_id) {
5221+
hash_map::Entry::Vacant(entry) => {
5222+
new_intercept_events.push(events::Event::HTLCIntercepted {
5223+
requested_next_hop_scid: scid,
5224+
payment_hash: forward_info.payment_hash,
5225+
inbound_amount_msat: forward_info.incoming_amt_msat.unwrap(),
5226+
expected_outbound_amount_msat: forward_info.outgoing_amt_msat,
5227+
intercept_id
5228+
});
5229+
entry.insert(PendingAddHTLCInfo {
5230+
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info });
5231+
},
5232+
hash_map::Entry::Occupied(_) => {
5233+
log_info!(self.logger, "Failed to forward incoming HTLC: detected duplicate intercepted payment over short channel id {}", scid);
5234+
let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
5235+
short_channel_id: prev_short_channel_id,
5236+
outpoint: prev_funding_outpoint,
5237+
htlc_id: prev_htlc_id,
5238+
incoming_packet_shared_secret: forward_info.incoming_shared_secret,
5239+
phantom_shared_secret: None,
5240+
});
5241+
5242+
failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
5243+
HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() },
5244+
HTLCDestination::InvalidForward { requested_forward_scid: scid },
5245+
));
5246+
}
5247+
}
5248+
} else {
5249+
// We don't want to generate a PendingHTLCsForwardable event if only intercepted
5250+
// payments are being processed.
5251+
if forward_htlcs_empty {
5252+
forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
5253+
}
5254+
entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
5255+
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
5256+
}
50885257
}
50895258
}
50905259
}
50915260
}
5261+
5262+
for (htlc_source, payment_hash, failure_reason, destination) in failed_intercept_forwards.drain(..) {
5263+
self.fail_htlc_backwards_internal(htlc_source, &payment_hash, failure_reason, destination);
5264+
}
5265+
5266+
if !new_intercept_events.is_empty() {
5267+
let mut events = self.pending_events.lock().unwrap();
5268+
events.append(&mut new_intercept_events);
5269+
}
5270+
50925271
match forward_event {
50935272
Some(time) => {
50945273
let mut pending_events = self.pending_events.lock().unwrap();
@@ -5690,6 +5869,23 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
56905869
}
56915870
}
56925871

5872+
/// Gets a fake short channel id for use in receiving intercepted payments. These fake scids are
5873+
/// used when constructing the route hints for HTLCs intended to be intercepted. See
5874+
/// [`ChannelManager::forward_intercepted_htlc`].
5875+
///
5876+
/// Note that this method is not guaranteed to return unique values, you may need to call it a few
5877+
/// times to get a unique scid.
5878+
pub fn get_intercept_scid(&self) -> u64 {
5879+
let best_block_height = self.best_block.read().unwrap().height();
5880+
let short_to_chan_info = self.short_to_chan_info.read().unwrap();
5881+
loop {
5882+
let scid_candidate = fake_scid::Namespace::Intercept.get_fake_scid(best_block_height, &self.genesis_hash, &self.fake_scid_rand_bytes, &self.keys_manager);
5883+
// Ensure the generated scid doesn't conflict with a real channel.
5884+
if short_to_chan_info.contains_key(&scid_candidate) { continue }
5885+
return scid_candidate
5886+
}
5887+
}
5888+
56935889
/// Gets inflight HTLC information by processing pending outbound payments that are in
56945890
/// our channels. May be used during pathfinding to account for in-use channel liquidity.
56955891
pub fn compute_inflight_htlcs(&self) -> InFlightHtlcs {
@@ -6073,7 +6269,6 @@ where
60736269
if height >= htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER {
60746270
let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
60756271
htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(height));
6076-
60776272
timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(), HTLCFailReason::Reason {
60786273
failure_code: 0x4000 | 15,
60796274
data: htlc_msat_height_data
@@ -6083,6 +6278,29 @@ where
60836278
});
60846279
!htlcs.is_empty() // Only retain this entry if htlcs has at least one entry.
60856280
});
6281+
6282+
let mut intercepted_htlcs = self.pending_intercepted_htlcs.lock().unwrap();
6283+
intercepted_htlcs.retain(|_, htlc| {
6284+
if height >= htlc.forward_info.outgoing_cltv_value - HTLC_FAIL_BACK_BUFFER {
6285+
let prev_hop_data = HTLCSource::PreviousHopData(HTLCPreviousHopData {
6286+
short_channel_id: htlc.prev_short_channel_id,
6287+
htlc_id: htlc.prev_htlc_id,
6288+
incoming_packet_shared_secret: htlc.forward_info.incoming_shared_secret,
6289+
phantom_shared_secret: None,
6290+
outpoint: htlc.prev_funding_outpoint,
6291+
});
6292+
6293+
let requested_forward_scid /* intercept scid */ = match htlc.forward_info.routing {
6294+
PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
6295+
_ => unreachable!(),
6296+
};
6297+
timed_out_htlcs.push((prev_hop_data, htlc.forward_info.payment_hash,
6298+
HTLCFailReason::Reason { failure_code: 0x2000 | 2, data: Vec::new() },
6299+
HTLCDestination::InvalidForward { requested_forward_scid }));
6300+
log_trace!(self.logger, "Timing out intercepted HTLC with requested forward scid {}", requested_forward_scid);
6301+
false
6302+
} else { true }
6303+
});
60866304
}
60876305

60886306
self.handle_init_event_channel_failures(failed_channels);
@@ -6991,8 +7209,15 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable for ChannelMana
69917209
_ => {},
69927210
}
69937211
}
7212+
7213+
let mut pending_intercepted_htlcs = None;
7214+
let our_pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
7215+
if our_pending_intercepts.len() != 0 {
7216+
pending_intercepted_htlcs = Some(our_pending_intercepts);
7217+
}
69947218
write_tlv_fields!(writer, {
69957219
(1, pending_outbound_payments_no_retry, required),
7220+
(2, pending_intercepted_htlcs, option),
69967221
(3, pending_outbound_payments, required),
69977222
(5, self.our_network_pubkey, required),
69987223
(7, self.fake_scid_rand_bytes, required),
@@ -7306,12 +7531,14 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
73067531
// pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
73077532
let mut pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>> = None;
73087533
let mut pending_outbound_payments = None;
7534+
let mut pending_intercepted_htlcs: Option<HashMap<InterceptId, PendingAddHTLCInfo>> = Some(HashMap::new());
73097535
let mut received_network_pubkey: Option<PublicKey> = None;
73107536
let mut fake_scid_rand_bytes: Option<[u8; 32]> = None;
73117537
let mut probing_cookie_secret: Option<[u8; 32]> = None;
73127538
let mut claimable_htlc_purposes = None;
73137539
read_tlv_fields!(reader, {
73147540
(1, pending_outbound_payments_no_retry, option),
7541+
(2, pending_intercepted_htlcs, option),
73157542
(3, pending_outbound_payments, option),
73167543
(5, received_network_pubkey, option),
73177544
(7, fake_scid_rand_bytes, option),
@@ -7534,6 +7761,7 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
75347761
inbound_payment_key: expanded_inbound_key,
75357762
pending_inbound_payments: Mutex::new(pending_inbound_payments),
75367763
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
7764+
pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),
75377765

75387766
forward_htlcs: Mutex::new(forward_htlcs),
75397767
claimable_htlcs: Mutex::new(claimable_htlcs),

0 commit comments

Comments
 (0)