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>
This commit is contained in:
Valentine Wallace 2022-11-04 16:23:47 -04:00
parent 5efc1976cd
commit 8fe7cbe921
No known key found for this signature in database
GPG key ID: FD3E106A2CE099B4
2 changed files with 83 additions and 16 deletions

View file

@ -92,8 +92,8 @@ use core::ops::Deref;
pub(super) enum PendingHTLCRouting {
Forward {
onion_packet: msgs::OnionPacket,
/// The SCID from the onion that we should forward to. This could be a "real" SCID, an
/// outbound SCID alias, or a phantom node SCID.
/// The SCID from the onion that we should forward to. This could be a real SCID or a fake one
/// generated using `get_fake_scid` from the scid_utils::fake_scid module.
short_channel_id: u64, // This should be NonZero<u64> eventually when we bump MSRV
},
Receive {
@ -684,6 +684,8 @@ pub type SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, M, T, F, L> = ChannelManage
// `total_consistency_lock`
// |
// |__`forward_htlcs`
// | |
// | |__`pending_intercepted_htlcs`
// |
// |__`pending_inbound_payments`
// | |
@ -2230,8 +2232,10 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
let forwarding_id_opt = match id_option {
None => { // unknown_next_peer
// Note that this is likely a timing oracle for detecting whether an scid is a
// phantom.
if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) {
// phantom or an intercept.
if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) ||
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)
{
None
} else {
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
fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) {
for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
let mut forward_event = None;
let mut new_intercept_events = Vec::new();
let mut failed_intercept_forwards = Vec::new();
if !pending_forwards.is_empty() {
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
if forward_htlcs.is_empty() {
forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS))
}
for (forward_info, prev_htlc_id) in pending_forwards.drain(..) {
match forward_htlcs.entry(match forward_info.routing {
PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
PendingHTLCRouting::Receive { .. } => 0,
PendingHTLCRouting::ReceiveKeysend { .. } => 0,
}) {
let scid = match forward_info.routing {
PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
PendingHTLCRouting::Receive { .. } => 0,
PendingHTLCRouting::ReceiveKeysend { .. } => 0,
};
// Pull this now to avoid introducing a lock order with `forward_htlcs`.
let is_our_scid = self.short_to_chan_info.read().unwrap().contains_key(&scid);
let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
let forward_htlcs_empty = forward_htlcs.is_empty();
match forward_htlcs.entry(scid) {
hash_map::Entry::Occupied(mut entry) => {
entry.get_mut().push(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }));
},
hash_map::Entry::Vacant(entry) => {
entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
if !is_our_scid && forward_info.incoming_amt_msat.is_some() &&
fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, scid, &self.genesis_hash)
{
let intercept_id = InterceptId(Sha256::hash(&forward_info.incoming_shared_secret).into_inner());
let mut pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
match pending_intercepts.entry(intercept_id) {
hash_map::Entry::Vacant(entry) => {
new_intercept_events.push(events::Event::HTLCIntercepted {
requested_next_hop_scid: scid,
payment_hash: forward_info.payment_hash,
inbound_amount_msat: forward_info.incoming_amt_msat.unwrap(),
expected_outbound_amount_msat: forward_info.outgoing_amt_msat,
intercept_id
});
entry.insert(PendingAddHTLCInfo {
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info });
},
hash_map::Entry::Occupied(_) => {
log_info!(self.logger, "Failed to forward incoming HTLC: detected duplicate intercepted payment over short channel id {}", scid);
let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
short_channel_id: prev_short_channel_id,
outpoint: prev_funding_outpoint,
htlc_id: prev_htlc_id,
incoming_packet_shared_secret: forward_info.incoming_shared_secret,
phantom_shared_secret: None,
});
failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() },
HTLCDestination::InvalidForward { requested_forward_scid: scid },
));
}
}
} else {
// We don't want to generate a PendingHTLCsForwardable event if only intercepted
// payments are being processed.
if forward_htlcs_empty {
forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
}
entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
}
}
}
}
}
for (htlc_source, payment_hash, failure_reason, destination) in failed_intercept_forwards.drain(..) {
self.fail_htlc_backwards_internal(htlc_source, &payment_hash, failure_reason, destination);
}
if !new_intercept_events.is_empty() {
let mut events = self.pending_events.lock().unwrap();
events.append(&mut new_intercept_events);
}
match forward_event {
Some(time) => {
let mut pending_events = self.pending_events.lock().unwrap();

View file

@ -182,6 +182,12 @@ pub enum HTLCDestination {
/// Short channel id we are requesting to forward an HTLC to.
requested_forward_scid: u64,
},
/// We couldn't forward to the outgoing scid. An example would be attempting to send a duplicate
/// intercept HTLC.
InvalidForward {
/// Short channel id we are requesting to forward an HTLC to.
requested_forward_scid: u64
},
/// Failure scenario where an HTLC may have been forwarded to be intended for us,
/// but is invalid for some reason, so we reject it.
///
@ -200,12 +206,15 @@ impl_writeable_tlv_based_enum_upgradable!(HTLCDestination,
(0, node_id, required),
(2, channel_id, required),
},
(1, InvalidForward) => {
(0, requested_forward_scid, required),
},
(2, UnknownNextHop) => {
(0, requested_forward_scid, required),
},
(4, FailedPayment) => {
(0, payment_hash, required),
}
},
);
#[cfg(anchors)]