Skip to content

Commit 8fe7cbe

Browse files
Generate HTLCIntercepted event upon interceptable forward
And store the pending intercepted HTLC in pending_intercepted_htlcs Co-authored-by: John Cantrell <johncantrell97@gmail.com> Co-authored-by: Valentine Wallace <vwallace@protonmail.com>
1 parent 5efc197 commit 8fe7cbe

File tree

2 files changed

+83
-16
lines changed

2 files changed

+83
-16
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 73 additions & 15 deletions
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 {
@@ -684,6 +684,8 @@ pub type SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, M, T, F, L> = ChannelManage
684684
// `total_consistency_lock`
685685
// |
686686
// |__`forward_htlcs`
687+
// | |
688+
// | |__`pending_intercepted_htlcs`
687689
// |
688690
// |__`pending_inbound_payments`
689691
// | |
@@ -2230,8 +2232,10 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
22302232
let forwarding_id_opt = match id_option {
22312233
None => { // unknown_next_peer
22322234
// Note that this is likely a timing oracle for detecting whether an scid is a
2233-
// phantom.
2234-
if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) {
2235+
// phantom or an intercept.
2236+
if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) ||
2237+
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)
2238+
{
22352239
None
22362240
} else {
22372241
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
@@ -5091,28 +5095,82 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
50915095
fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) {
50925096
for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
50935097
let mut forward_event = None;
5098+
let mut new_intercept_events = Vec::new();
5099+
let mut failed_intercept_forwards = Vec::new();
50945100
if !pending_forwards.is_empty() {
5095-
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
5096-
if forward_htlcs.is_empty() {
5097-
forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS))
5098-
}
50995101
for (forward_info, prev_htlc_id) in pending_forwards.drain(..) {
5100-
match forward_htlcs.entry(match forward_info.routing {
5101-
PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
5102-
PendingHTLCRouting::Receive { .. } => 0,
5103-
PendingHTLCRouting::ReceiveKeysend { .. } => 0,
5104-
}) {
5102+
let scid = match forward_info.routing {
5103+
PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
5104+
PendingHTLCRouting::Receive { .. } => 0,
5105+
PendingHTLCRouting::ReceiveKeysend { .. } => 0,
5106+
};
5107+
// Pull this now to avoid introducing a lock order with `forward_htlcs`.
5108+
let is_our_scid = self.short_to_chan_info.read().unwrap().contains_key(&scid);
5109+
5110+
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
5111+
let forward_htlcs_empty = forward_htlcs.is_empty();
5112+
match forward_htlcs.entry(scid) {
51055113
hash_map::Entry::Occupied(mut entry) => {
51065114
entry.get_mut().push(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
51075115
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }));
51085116
},
51095117
hash_map::Entry::Vacant(entry) => {
5110-
entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
5111-
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
5118+
if !is_our_scid && forward_info.incoming_amt_msat.is_some() &&
5119+
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, scid, &self.genesis_hash)
5120+
{
5121+
let intercept_id = InterceptId(Sha256::hash(&forward_info.incoming_shared_secret).into_inner());
5122+
let mut pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
5123+
match pending_intercepts.entry(intercept_id) {
5124+
hash_map::Entry::Vacant(entry) => {
5125+
new_intercept_events.push(events::Event::HTLCIntercepted {
5126+
requested_next_hop_scid: scid,
5127+
payment_hash: forward_info.payment_hash,
5128+
inbound_amount_msat: forward_info.incoming_amt_msat.unwrap(),
5129+
expected_outbound_amount_msat: forward_info.outgoing_amt_msat,
5130+
intercept_id
5131+
});
5132+
entry.insert(PendingAddHTLCInfo {
5133+
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info });
5134+
},
5135+
hash_map::Entry::Occupied(_) => {
5136+
log_info!(self.logger, "Failed to forward incoming HTLC: detected duplicate intercepted payment over short channel id {}", scid);
5137+
let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
5138+
short_channel_id: prev_short_channel_id,
5139+
outpoint: prev_funding_outpoint,
5140+
htlc_id: prev_htlc_id,
5141+
incoming_packet_shared_secret: forward_info.incoming_shared_secret,
5142+
phantom_shared_secret: None,
5143+
});
5144+
5145+
failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
5146+
HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() },
5147+
HTLCDestination::InvalidForward { requested_forward_scid: scid },
5148+
));
5149+
}
5150+
}
5151+
} else {
5152+
// We don't want to generate a PendingHTLCsForwardable event if only intercepted
5153+
// payments are being processed.
5154+
if forward_htlcs_empty {
5155+
forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
5156+
}
5157+
entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
5158+
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
5159+
}
51125160
}
51135161
}
51145162
}
51155163
}
5164+
5165+
for (htlc_source, payment_hash, failure_reason, destination) in failed_intercept_forwards.drain(..) {
5166+
self.fail_htlc_backwards_internal(htlc_source, &payment_hash, failure_reason, destination);
5167+
}
5168+
5169+
if !new_intercept_events.is_empty() {
5170+
let mut events = self.pending_events.lock().unwrap();
5171+
events.append(&mut new_intercept_events);
5172+
}
5173+
51165174
match forward_event {
51175175
Some(time) => {
51185176
let mut pending_events = self.pending_events.lock().unwrap();

lightning/src/util/events.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,12 @@ pub enum HTLCDestination {
182182
/// Short channel id we are requesting to forward an HTLC to.
183183
requested_forward_scid: u64,
184184
},
185+
/// We couldn't forward to the outgoing scid. An example would be attempting to send a duplicate
186+
/// intercept HTLC.
187+
InvalidForward {
188+
/// Short channel id we are requesting to forward an HTLC to.
189+
requested_forward_scid: u64
190+
},
185191
/// Failure scenario where an HTLC may have been forwarded to be intended for us,
186192
/// but is invalid for some reason, so we reject it.
187193
///
@@ -200,12 +206,15 @@ impl_writeable_tlv_based_enum_upgradable!(HTLCDestination,
200206
(0, node_id, required),
201207
(2, channel_id, required),
202208
},
209+
(1, InvalidForward) => {
210+
(0, requested_forward_scid, required),
211+
},
203212
(2, UnknownNextHop) => {
204213
(0, requested_forward_scid, required),
205214
},
206215
(4, FailedPayment) => {
207216
(0, payment_hash, required),
208-
}
217+
},
209218
);
210219

211220
#[cfg(anchors)]

0 commit comments

Comments
 (0)