From: Valentine Wallace Date: Fri, 4 Nov 2022 20:23:47 +0000 (-0400) Subject: Generate HTLCIntercepted event upon interceptable forward X-Git-Tag: v0.0.113~22^2~5 X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=8fe7cbe9216975bfdb783af2b11203400e0a2d21;p=rust-lightning Generate HTLCIntercepted event upon interceptable forward And store the pending intercepted HTLC in pending_intercepted_htlcs Co-authored-by: John Cantrell Co-authored-by: Valentine Wallace --- diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 58db00e14..d80231897 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -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 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 ChannelManager { // 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 ChannelManager)]) { 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(); diff --git a/lightning/src/util/events.rs b/lightning/src/util/events.rs index 04a3b1004..268f9c908 100644 --- a/lightning/src/util/events.rs +++ b/lightning/src/util/events.rs @@ -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)]