Skip to content

Commit fe1a227

Browse files
Generate HTLCIntercepted event upon interceptable forward
And store the pending intercepted HTLC in pending_intercepted_htlcs Co-authored-by: John Cantrell <[email protected]> Co-authored-by: Valentine Wallace <[email protected]>
1 parent d761469 commit fe1a227

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 {
@@ -683,6 +683,8 @@ pub type SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, M, T, F, L> = ChannelManage
683683
// `total_consistency_lock`
684684
// |
685685
// |__`forward_htlcs`
686+
// | |
687+
// | |__`pending_intercepted_htlcs`
686688
// |
687689
// |__`pending_inbound_payments`
688690
// | |
@@ -2223,8 +2225,10 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
22232225
let forwarding_id_opt = match id_option {
22242226
None => { // unknown_next_peer
22252227
// Note that this is likely a timing oracle for detecting whether an scid is a
2226-
// phantom.
2227-
if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) {
2228+
// phantom or an intercept.
2229+
if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) ||
2230+
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)
2231+
{
22282232
None
22292233
} else {
22302234
break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
@@ -5083,28 +5087,82 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
50835087
fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, Vec<(PendingHTLCInfo, u64)>)]) {
50845088
for &mut (prev_short_channel_id, prev_funding_outpoint, ref mut pending_forwards) in per_source_pending_forwards {
50855089
let mut forward_event = None;
5090+
let mut new_intercept_events = Vec::new();
5091+
let mut failed_intercept_forwards = Vec::new();
50865092
if !pending_forwards.is_empty() {
5087-
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
5088-
if forward_htlcs.is_empty() {
5089-
forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS))
5090-
}
50915093
for (forward_info, prev_htlc_id) in pending_forwards.drain(..) {
5092-
match forward_htlcs.entry(match forward_info.routing {
5093-
PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
5094-
PendingHTLCRouting::Receive { .. } => 0,
5095-
PendingHTLCRouting::ReceiveKeysend { .. } => 0,
5096-
}) {
5094+
let scid = match forward_info.routing {
5095+
PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
5096+
PendingHTLCRouting::Receive { .. } => 0,
5097+
PendingHTLCRouting::ReceiveKeysend { .. } => 0,
5098+
};
5099+
// Pull this now to avoid introducing a lock order with `forward_htlcs`.
5100+
let is_our_scid = self.short_to_chan_info.read().unwrap().contains_key(&scid);
5101+
5102+
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
5103+
let forward_htlcs_empty = forward_htlcs.is_empty();
5104+
match forward_htlcs.entry(scid) {
50975105
hash_map::Entry::Occupied(mut entry) => {
50985106
entry.get_mut().push(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
50995107
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, forward_info }));
51005108
},
51015109
hash_map::Entry::Vacant(entry) => {
5102-
entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
5103-
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, forward_info })));
5110+
if !is_our_scid && forward_info.incoming_amt_msat.is_some() &&
5111+
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, scid, &self.genesis_hash)
5112+
{
5113+
let intercept_id = InterceptId(Sha256::hash(&forward_info.incoming_shared_secret).into_inner());
5114+
let mut pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
5115+
match pending_intercepts.entry(intercept_id) {
5116+
hash_map::Entry::Vacant(entry) => {
5117+
new_intercept_events.push(events::Event::HTLCIntercepted {
5118+
requested_next_hop_scid: scid,
5119+
payment_hash: forward_info.payment_hash,
5120+
inbound_amount_msat: forward_info.incoming_amt_msat.unwrap(),
5121+
expected_outbound_amount_msat: forward_info.outgoing_amt_msat,
5122+
intercept_id
5123+
});
5124+
entry.insert(PendingAddHTLCInfo {
5125+
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, forward_info });
5126+
},
5127+
hash_map::Entry::Occupied(_) => {
5128+
log_info!(self.logger, "Failed to forward incoming HTLC: detected duplicate intercepted payment over short channel id {}", scid);
5129+
let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
5130+
short_channel_id: prev_short_channel_id,
5131+
outpoint: prev_funding_outpoint,
5132+
htlc_id: prev_htlc_id,
5133+
incoming_packet_shared_secret: forward_info.incoming_shared_secret,
5134+
phantom_shared_secret: None,
5135+
});
5136+
5137+
failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
5138+
HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() },
5139+
HTLCDestination::InvalidForward { requested_forward_scid: scid },
5140+
));
5141+
}
5142+
}
5143+
} else {
5144+
// We don't want to generate a PendingHTLCsForwardable event if only intercepted
5145+
// payments are being processed.
5146+
if forward_htlcs_empty {
5147+
forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
5148+
}
5149+
entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
5150+
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, forward_info })));
5151+
}
51045152
}
51055153
}
51065154
}
51075155
}
5156+
5157+
for (htlc_source, payment_hash, failure_reason, destination) in failed_intercept_forwards.drain(..) {
5158+
self.fail_htlc_backwards_internal(htlc_source, &payment_hash, failure_reason, destination);
5159+
}
5160+
5161+
if !new_intercept_events.is_empty() {
5162+
let mut events = self.pending_events.lock().unwrap();
5163+
events.append(&mut new_intercept_events);
5164+
}
5165+
51085166
match forward_event {
51095167
Some(time) => {
51105168
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)