X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchannelmanager.rs;h=01352ffa3871accfdaeeab629ec0d3dc24caa39b;hb=c1fbb90847f8c31a192f647d5262e0fc485f6550;hp=662b85bcca52a4a683932d54c32c48f3f299199d;hpb=7bb4a235bccc92e7edba42827a3f95644c3a02d9;p=rust-lightning diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 662b85bc..01352ffa 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -111,6 +111,7 @@ use crate::ln::script::ShutdownScript; /// Information about where a received HTLC('s onion) has indicated the HTLC should go. #[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug +#[cfg_attr(test, derive(Debug, PartialEq))] pub enum PendingHTLCRouting { /// An HTLC which should be forwarded on to another node. Forward { @@ -189,7 +190,7 @@ pub enum PendingHTLCRouting { } /// Information used to forward or fail this HTLC that is being forwarded within a blinded path. -#[derive(Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub struct BlindedForward { /// The `blinding_point` that was set in the inbound [`msgs::UpdateAddHTLC`], or in the inbound /// onion payload if we're the introduction node. Useful for calculating the next hop's @@ -213,6 +214,7 @@ impl PendingHTLCRouting { /// Information about an incoming HTLC, including the [`PendingHTLCRouting`] describing where it /// should go next. #[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug +#[cfg_attr(test, derive(Debug, PartialEq))] pub struct PendingHTLCInfo { /// Further routing details based on whether the HTLC is being forwarded or received. pub routing: PendingHTLCRouting, @@ -267,6 +269,7 @@ pub(super) enum PendingHTLCStatus { Fail(HTLCFailureMsg), } +#[cfg_attr(test, derive(Clone, Debug, PartialEq))] pub(super) struct PendingAddHTLCInfo { pub(super) forward_info: PendingHTLCInfo, @@ -282,6 +285,7 @@ pub(super) struct PendingAddHTLCInfo { prev_user_channel_id: u128, } +#[cfg_attr(test, derive(Clone, Debug, PartialEq))] pub(super) enum HTLCForwardInfo { AddHTLC(PendingAddHTLCInfo), FailHTLC { @@ -1528,13 +1532,11 @@ pub const MIN_FINAL_CLTV_EXPIRY_DELTA: u16 = HTLC_FAIL_BACK_BUFFER as u16 + 3; // then waiting ANTI_REORG_DELAY to be reorg-safe on the outbound HLTC and // failing the corresponding htlc backward, and us now seeing the last block of ANTI_REORG_DELAY before // LATENCY_GRACE_PERIOD_BLOCKS. -#[deny(const_err)] #[allow(dead_code)] const CHECK_CLTV_EXPIRY_SANITY: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - CLTV_CLAIM_BUFFER - ANTI_REORG_DELAY - LATENCY_GRACE_PERIOD_BLOCKS; // Check for ability of an attacker to make us fail on-chain by delaying an HTLC claim. See // ChannelMonitor::should_broadcast_holder_commitment_txn for a description of why this is needed. -#[deny(const_err)] #[allow(dead_code)] const CHECK_CLTV_EXPIRY_SANITY_2: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - 2*CLTV_CLAIM_BUFFER; @@ -3025,11 +3027,12 @@ where msg, &self.node_signer, &self.logger, &self.secp_ctx )?; - let is_blinded = match next_hop { + let is_intro_node_forward = match next_hop { onion_utils::Hop::Forward { + // TODO: update this when we support blinded forwarding as non-intro node next_hop_data: msgs::InboundOnionPayload::BlindedForward { .. }, .. } => true, - _ => false, // TODO: update this when we support receiving to multi-hop blinded paths + _ => false, }; macro_rules! return_err { @@ -3039,7 +3042,17 @@ where WithContext::from(&self.logger, Some(*counterparty_node_id), Some(msg.channel_id)), "Failed to accept/forward incoming HTLC: {}", $msg ); - let (err_code, err_data) = if is_blinded { + // If `msg.blinding_point` is set, we must always fail with malformed. + if msg.blinding_point.is_some() { + return Err(HTLCFailureMsg::Malformed(msgs::UpdateFailMalformedHTLC { + channel_id: msg.channel_id, + htlc_id: msg.htlc_id, + sha256_of_onion: [0; 32], + failure_code: INVALID_ONION_BLINDING, + })); + } + + let (err_code, err_data) = if is_intro_node_forward { (INVALID_ONION_BLINDING, &[0; 32][..]) } else { ($err_code, $data) }; return Err(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC { @@ -3192,6 +3205,16 @@ where { let logger = WithContext::from(&self.logger, Some(*counterparty_node_id), Some(msg.channel_id)); log_info!(logger, "Failed to accept/forward incoming HTLC: {}", $msg); + if msg.blinding_point.is_some() { + return PendingHTLCStatus::Fail(HTLCFailureMsg::Malformed( + msgs::UpdateFailMalformedHTLC { + channel_id: msg.channel_id, + htlc_id: msg.htlc_id, + sha256_of_onion: [0; 32], + failure_code: INVALID_ONION_BLINDING, + } + )) + } return PendingHTLCStatus::Fail(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC { channel_id: msg.channel_id, htlc_id: msg.htlc_id, @@ -4321,7 +4344,7 @@ where if let Some(ChannelPhase::Funded(ref mut chan)) = peer_state.channel_by_id.get_mut(&forward_chan_id) { let logger = WithChannelContext::from(&self.logger, &chan.context); for forward_info in pending_forwards.drain(..) { - match forward_info { + let queue_fail_htlc_res = match forward_info { HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo { prev_short_channel_id, prev_htlc_id, prev_funding_outpoint, prev_user_channel_id, forward_info: PendingHTLCInfo { @@ -4367,29 +4390,35 @@ where )); continue; } + None }, HTLCForwardInfo::AddHTLC { .. } => { panic!("short_channel_id != 0 should imply any pending_forward entries are of type Forward"); }, HTLCForwardInfo::FailHTLC { htlc_id, err_packet } => { log_trace!(logger, "Failing HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id); - if let Err(e) = chan.queue_fail_htlc( - htlc_id, err_packet, &&logger - ) { - if let ChannelError::Ignore(msg) = e { - log_trace!(logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg); - } else { - panic!("Stated return value requirements in queue_fail_htlc() were not met"); - } - // fail-backs are best-effort, we probably already have one - // pending, and if not that's OK, if not, the channel is on - // the chain and sending the HTLC-Timeout is their problem. - continue; - } + Some((chan.queue_fail_htlc(htlc_id, err_packet, &&logger), htlc_id)) }, - HTLCForwardInfo::FailMalformedHTLC { .. } => { - todo!() + HTLCForwardInfo::FailMalformedHTLC { htlc_id, failure_code, sha256_of_onion } => { + log_trace!(logger, "Failing malformed HTLC back to channel with short id {} (backward HTLC ID {}) after delay", short_chan_id, htlc_id); + let res = chan.queue_fail_malformed_htlc( + htlc_id, failure_code, sha256_of_onion, &&logger + ); + Some((res, htlc_id)) }, + }; + if let Some((queue_fail_htlc_res, htlc_id)) = queue_fail_htlc_res { + if let Err(e) = queue_fail_htlc_res { + if let ChannelError::Ignore(msg) = e { + log_trace!(logger, "Failed to fail HTLC with ID {} backwards to short_id {}: {}", htlc_id, short_chan_id, msg); + } else { + panic!("Stated return value requirements in queue_fail_{{malformed_}}htlc() were not met"); + } + // fail-backs are best-effort, we probably already have one + // pending, and if not that's OK, if not, the channel is on + // the chain and sending the HTLC-Timeout is their problem. + continue; + } } } } else { @@ -5249,16 +5278,26 @@ where "Failing {}HTLC with payment_hash {} backwards from us: {:?}", if blinded_failure.is_some() { "blinded " } else { "" }, &payment_hash, onion_error ); - let err_packet = match blinded_failure { + let failure = match blinded_failure { Some(BlindedFailure::FromIntroductionNode) => { let blinded_onion_error = HTLCFailReason::reason(INVALID_ONION_BLINDING, vec![0; 32]); - blinded_onion_error.get_encrypted_failure_packet( + let err_packet = blinded_onion_error.get_encrypted_failure_packet( incoming_packet_shared_secret, phantom_shared_secret - ) + ); + HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet } + }, + Some(BlindedFailure::FromBlindedNode) => { + HTLCForwardInfo::FailMalformedHTLC { + htlc_id: *htlc_id, + failure_code: INVALID_ONION_BLINDING, + sha256_of_onion: [0; 32] + } }, - Some(BlindedFailure::FromBlindedNode) => todo!(), None => { - onion_error.get_encrypted_failure_packet(incoming_packet_shared_secret, phantom_shared_secret) + let err_packet = onion_error.get_encrypted_failure_packet( + incoming_packet_shared_secret, phantom_shared_secret + ); + HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet } } }; @@ -5269,10 +5308,10 @@ where } match forward_htlcs.entry(*short_channel_id) { hash_map::Entry::Occupied(mut entry) => { - entry.get_mut().push(HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet }); + entry.get_mut().push(failure); }, hash_map::Entry::Vacant(entry) => { - entry.insert(vec!(HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet })); + entry.insert(vec!(failure)); } } mem::drop(forward_htlcs); @@ -6572,6 +6611,16 @@ where Err(e) => PendingHTLCStatus::Fail(e) }; let create_pending_htlc_status = |chan: &Channel, pending_forward_info: PendingHTLCStatus, error_code: u16| { + if msg.blinding_point.is_some() { + return PendingHTLCStatus::Fail(HTLCFailureMsg::Malformed( + msgs::UpdateFailMalformedHTLC { + channel_id: msg.channel_id, + htlc_id: msg.htlc_id, + sha256_of_onion: [0; 32], + failure_code: INVALID_ONION_BLINDING, + } + )) + } // If the update_add is completely bogus, the call will Err and we will close, // but if we've sent a shutdown and they haven't acknowledged it yet, we just // want to reject the new HTLC and fail it backwards instead of forwarding. @@ -10568,7 +10617,6 @@ where // 0.0.102+ for (_, monitor) in args.channel_monitors.iter() { let counterparty_opt = id_to_peer.get(&monitor.get_funding_txo().0.to_channel_id()); - let chan_id = monitor.get_funding_txo().0.to_channel_id(); if counterparty_opt.is_none() { let logger = WithChannelMonitor::from(&args.logger, monitor); for (htlc_source, (htlc, _)) in monitor.get_pending_or_resolved_outbound_htlcs() { @@ -11004,12 +11052,14 @@ mod tests { use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, ClosureReason}; use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret}; use crate::ln::ChannelId; - use crate::ln::channelmanager::{create_recv_pending_htlc_info, inbound_payment, PaymentId, PaymentSendFailure, RecipientOnionFields, InterceptId}; + use crate::ln::channelmanager::{create_recv_pending_htlc_info, HTLCForwardInfo, inbound_payment, PaymentId, PaymentSendFailure, RecipientOnionFields, InterceptId}; use crate::ln::functional_test_utils::*; use crate::ln::msgs::{self, ErrorAction}; use crate::ln::msgs::ChannelMessageHandler; + use crate::prelude::*; use crate::routing::router::{PaymentParameters, RouteParameters, find_route}; use crate::util::errors::APIError; + use crate::util::ser::Writeable; use crate::util::test_utils; use crate::util::config::{ChannelConfig, ChannelConfigUpdate}; use crate::sign::EntropySource; @@ -12284,6 +12334,63 @@ mod tests { check_spends!(txn[0], funding_tx); } } + + #[test] + fn test_malformed_forward_htlcs_ser() { + // Ensure that `HTLCForwardInfo::FailMalformedHTLC`s are (de)serialized properly. + let chanmon_cfg = create_chanmon_cfgs(1); + let node_cfg = create_node_cfgs(1, &chanmon_cfg); + let persister; + let chain_monitor; + let chanmgrs = create_node_chanmgrs(1, &node_cfg, &[None]); + let deserialized_chanmgr; + let mut nodes = create_network(1, &node_cfg, &chanmgrs); + + let dummy_failed_htlc = |htlc_id| { + HTLCForwardInfo::FailHTLC { htlc_id, err_packet: msgs::OnionErrorPacket { data: vec![42] }, } + }; + let dummy_malformed_htlc = |htlc_id| { + HTLCForwardInfo::FailMalformedHTLC { htlc_id, failure_code: 0x4000, sha256_of_onion: [0; 32] } + }; + + let dummy_htlcs_1: Vec = (1..10).map(|htlc_id| { + if htlc_id % 2 == 0 { + dummy_failed_htlc(htlc_id) + } else { + dummy_malformed_htlc(htlc_id) + } + }).collect(); + + let dummy_htlcs_2: Vec = (1..10).map(|htlc_id| { + if htlc_id % 2 == 1 { + dummy_failed_htlc(htlc_id) + } else { + dummy_malformed_htlc(htlc_id) + } + }).collect(); + + + let (scid_1, scid_2) = (42, 43); + let mut forward_htlcs = HashMap::new(); + forward_htlcs.insert(scid_1, dummy_htlcs_1.clone()); + forward_htlcs.insert(scid_2, dummy_htlcs_2.clone()); + + let mut chanmgr_fwd_htlcs = nodes[0].node.forward_htlcs.lock().unwrap(); + *chanmgr_fwd_htlcs = forward_htlcs.clone(); + core::mem::drop(chanmgr_fwd_htlcs); + + reload_node!(nodes[0], nodes[0].node.encode(), &[], persister, chain_monitor, deserialized_chanmgr); + + let mut deserialized_fwd_htlcs = nodes[0].node.forward_htlcs.lock().unwrap(); + for scid in [scid_1, scid_2].iter() { + let deserialized_htlcs = deserialized_fwd_htlcs.remove(scid).unwrap(); + assert_eq!(forward_htlcs.remove(scid).unwrap(), deserialized_htlcs); + } + assert!(deserialized_fwd_htlcs.is_empty()); + core::mem::drop(deserialized_fwd_htlcs); + + expect_pending_htlcs_forwardable!(nodes[0]); + } } #[cfg(ldk_bench)]