X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchannelmanager.rs;h=d802318975e6609adc33f7f50b873afa6108aad5;hb=8fe7cbe9216975bfdb783af2b11203400e0a2d21;hp=d788356d63c23fe312452a6560e0597c69cf5e58;hpb=3e88b72c50bf52626ded205735181a21ef273b2a;p=rust-lightning diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index d788356d..d8023189 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 { @@ -207,6 +207,24 @@ impl Readable for PaymentId { Ok(PaymentId(buf)) } } + +/// An identifier used to uniquely identify an intercepted HTLC to LDK. +/// (C-not exported) as we just use [u8; 32] directly +#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)] +pub struct InterceptId(pub [u8; 32]); + +impl Writeable for InterceptId { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.0.write(w) + } +} + +impl Readable for InterceptId { + fn read(r: &mut R) -> Result { + let buf: [u8; 32] = Readable::read(r)?; + Ok(InterceptId(buf)) + } +} /// Tracks the inbound corresponding to an outbound HTLC #[allow(clippy::derive_hash_xor_eq)] // Our Hash is faithful to the data, we just don't have SecretKey::hash #[derive(Clone, PartialEq, Eq)] @@ -666,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` // | | @@ -751,6 +771,11 @@ pub struct ChannelManager pub(super) forward_htlcs: Mutex>>, #[cfg(not(test))] forward_htlcs: Mutex>>, + /// Storage for HTLCs that have been intercepted and bubbled up to the user. We hold them here + /// until the user tells us what we should do with them. + /// + /// See `ChannelManager` struct-level documentation for lock order requirements. + pending_intercepted_htlcs: Mutex>, /// Map from payment hash to the payment data and any HTLCs which are to us and can be /// failed/claimed by the user. @@ -1566,6 +1591,7 @@ impl ChannelManager 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)); @@ -5067,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(); @@ -6991,8 +7073,15 @@ impl Writeable for ChannelMana _ => {}, } } + + let mut pending_intercepted_htlcs = None; + let our_pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap(); + if our_pending_intercepts.len() != 0 { + pending_intercepted_htlcs = Some(our_pending_intercepts); + } write_tlv_fields!(writer, { (1, pending_outbound_payments_no_retry, required), + (2, pending_intercepted_htlcs, option), (3, pending_outbound_payments, required), (5, self.our_network_pubkey, required), (7, self.fake_scid_rand_bytes, required), @@ -7306,12 +7395,14 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> // pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients. let mut pending_outbound_payments_no_retry: Option>> = None; let mut pending_outbound_payments = None; + let mut pending_intercepted_htlcs: Option> = Some(HashMap::new()); let mut received_network_pubkey: Option = None; let mut fake_scid_rand_bytes: Option<[u8; 32]> = None; let mut probing_cookie_secret: Option<[u8; 32]> = None; let mut claimable_htlc_purposes = None; read_tlv_fields!(reader, { (1, pending_outbound_payments_no_retry, option), + (2, pending_intercepted_htlcs, option), (3, pending_outbound_payments, option), (5, received_network_pubkey, option), (7, fake_scid_rand_bytes, option), @@ -7534,6 +7625,7 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> inbound_payment_key: expanded_inbound_key, pending_inbound_payments: Mutex::new(pending_inbound_payments), pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()), + pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()), forward_htlcs: Mutex::new(forward_htlcs), claimable_htlcs: Mutex::new(claimable_htlcs),