From: Matt Corallo Date: Tue, 24 Apr 2018 04:18:54 +0000 (-0400) Subject: HTLC Failure message handling X-Git-Tag: v0.0.12~411^2~3 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=8e2974fc2a5871c33584f8b5c90b65a8450f2a5d;p=rust-lightning HTLC Failure message handling --- diff --git a/src/ln/channel.rs b/src/ln/channel.rs index 1a2dd0606..fcdb9fcb9 100644 --- a/src/ln/channel.rs +++ b/src/ln/channel.rs @@ -1315,7 +1315,8 @@ impl Channel { } /// Removes an outbound HTLC which has been commitment_signed by the remote end - fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option) -> Result<(), HandleError> { + #[inline] + fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<[u8; 32]>, fail_reason: Option) -> Result<[u8; 32], HandleError> { for mut htlc in self.pending_htlcs.iter_mut() { if htlc.outbound && htlc.htlc_id == htlc_id { match check_preimage { @@ -1335,7 +1336,7 @@ impl Channel { } else { panic!("Got a non-outbound state on an outbound HTLC"); } - return Ok(()); + return Ok(htlc.payment_hash.clone()); } } Err(HandleError{err: "Remote tried to fulfill/fail an HTLC we couldn't find", msg: None}) @@ -1352,10 +1353,11 @@ impl Channel { sha.result(&mut payment_hash); self.channel_monitor.provide_payment_preimage(&msg.payment_preimage); - self.mark_outbound_htlc_removed(msg.htlc_id, Some(payment_hash), None) + self.mark_outbound_htlc_removed(msg.htlc_id, Some(payment_hash), None)?; + Ok(()) } - pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<(), HandleError> { + pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<[u8; 32], HandleError> { if (self.channel_state & (ChannelState::ChannelFunded as u32)) != (ChannelState::ChannelFunded as u32) { return Err(HandleError{err: "Got add HTLC message when channel was not in an operational state", msg: None}); } @@ -1368,7 +1370,8 @@ impl Channel { return Err(HandleError{err: "Got add HTLC message when channel was not in an operational state", msg: None}); } - self.mark_outbound_htlc_removed(msg.htlc_id, None, Some(fail_reason)) + self.mark_outbound_htlc_removed(msg.htlc_id, None, Some(fail_reason))?; + Ok(()) } pub fn commitment_signed(&mut self, msg: &msgs::CommitmentSigned) -> Result<(msgs::RevokeAndACK, Option), HandleError> { diff --git a/src/ln/channelmanager.rs b/src/ln/channelmanager.rs index dda41c9a4..448823201 100644 --- a/src/ln/channelmanager.rs +++ b/src/ln/channelmanager.rs @@ -14,12 +14,13 @@ use secp256k1; use chain::chaininterface::{BroadcasterInterface,ChainListener,ChainWatchInterface,FeeEstimator}; use ln::channel::{Channel, ChannelKeys}; use ln::channelmonitor::ManyChannelMonitor; -use ln::router::Route; +use ln::router::{Route,RouteHop}; use ln::msgs; use ln::msgs::{HandleError,ChannelMessageHandler,MsgEncodable,MsgDecodable}; use util::{byte_utils, events, internal_traits, rng}; use util::sha2::Sha256; +use crypto; use crypto::mac::{Mac,MacResult}; use crypto::hmac::Hmac; use crypto::digest::Digest; @@ -91,12 +92,14 @@ enum PendingOutboundHTLC { }, OutboundRoute { route: Route, + session_priv: SecretKey, }, /// Used for channel rebalancing CycledRoute { source_short_channel_id: u64, incoming_packet_shared_secret: SharedSecret, route: Route, + session_priv: SecretKey, } } @@ -336,8 +339,9 @@ impl ChannelManager { res } - fn construct_onion_keys(secp_ctx: &Secp256k1, route: &Route, session_priv: &SecretKey) -> Result, HandleError> { - let mut res = Vec::with_capacity(route.hops.len()); + // can only fail if an intermediary hop has an invalid public key or session_priv is invalid + #[inline] + fn construct_onion_keys_callback (secp_ctx: &Secp256k1, route: &Route, session_priv: &SecretKey, mut callback: FType) -> Result<(), HandleError> { let mut blinded_priv = session_priv.clone(); let mut blinded_pub = secp_call!(PublicKey::from_secret_key(secp_ctx, &blinded_priv)); let mut first_iteration = true; @@ -360,18 +364,29 @@ impl ChannelManager { secp_call!(blinded_priv.mul_assign(secp_ctx, &secp_call!(SecretKey::from_slice(secp_ctx, &blinding_factor)))); blinded_pub = secp_call!(PublicKey::from_secret_key(secp_ctx, &blinded_priv)); + callback(shared_secret, blinding_factor, ephemeral_pubkey, hop); + } + + Ok(()) + } + + // can only fail if an intermediary hop has an invalid public key or session_priv is invalid + fn construct_onion_keys(secp_ctx: &Secp256k1, route: &Route, session_priv: &SecretKey) -> Result, HandleError> { + let mut res = Vec::with_capacity(route.hops.len()); + + Self::construct_onion_keys_callback(secp_ctx, route, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _| { let (rho, mu) = ChannelManager::gen_rho_mu_from_shared_secret(&shared_secret); res.push(OnionKeys { #[cfg(test)] - shared_secret: shared_secret, + shared_secret, #[cfg(test)] - blinding_factor: blinding_factor, - ephemeral_pubkey: ephemeral_pubkey, - rho: rho, - mu: mu, + blinding_factor: _blinding_factor, + ephemeral_pubkey, + rho, + mu, }); - } + })?; Ok(res) } @@ -602,7 +617,8 @@ impl ChannelManager { }; if channel_state.claimable_htlcs.insert(payment_hash, PendingOutboundHTLC::OutboundRoute { - route: route, + route, + session_priv, }).is_some() { // TODO: We need to track these better, we're not generating these, so a // third-party might make this happen: @@ -747,7 +763,7 @@ impl ChannelManager { for failed_forward in failed_forwards.drain(..) { match failed_forward.2 { None => self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), &failed_forward.0, HTLCFailReason::Reason { failure_code: failed_forward.1, data: Vec::new() }), - Some(chan_update) => self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), &failed_forward.0, HTLCFailReason::Reason { failure_code: failed_forward.1, data: chan_update.encode() }), + Some(chan_update) => self.fail_htlc_backwards_internal(self.channel_state.lock().unwrap(), &failed_forward.0, HTLCFailReason::Reason { failure_code: failed_forward.1, data: chan_update.encode_with_len() }), }; } @@ -774,7 +790,11 @@ impl ChannelManager { }; match pending_htlc { - PendingOutboundHTLC::CycledRoute { source_short_channel_id, incoming_packet_shared_secret, .. } => { + PendingOutboundHTLC::CycledRoute { source_short_channel_id, incoming_packet_shared_secret, route, session_priv } => { + channel_state.claimable_htlcs.insert(payment_hash.clone(), PendingOutboundHTLC::OutboundRoute { + route, + session_priv, + }); pending_htlc = PendingOutboundHTLC::IntermediaryHopData { source_short_channel_id, incoming_packet_shared_secret }; }, _ => {} @@ -783,8 +803,8 @@ impl ChannelManager { match pending_htlc { PendingOutboundHTLC::CycledRoute { .. } => { panic!("WAT"); }, PendingOutboundHTLC::OutboundRoute { .. } => { - //TODO: DECRYPT route from OutboundRoute mem::drop(channel_state); + let mut pending_events = self.pending_events.lock().unwrap(); pending_events.push(events::Event::PaymentFailed { payment_hash: payment_hash.clone() @@ -858,13 +878,13 @@ impl ChannelManager { }; match pending_htlc { - PendingOutboundHTLC::CycledRoute { source_short_channel_id, incoming_packet_shared_secret, route } => { + PendingOutboundHTLC::CycledRoute { source_short_channel_id, incoming_packet_shared_secret, route, session_priv } => { if from_user { // This was the end hop back to us pending_htlc = PendingOutboundHTLC::IntermediaryHopData { source_short_channel_id, incoming_packet_shared_secret }; - channel_state.claimable_htlcs.insert(payment_hash, PendingOutboundHTLC::OutboundRoute { route }); + channel_state.claimable_htlcs.insert(payment_hash, PendingOutboundHTLC::OutboundRoute { route, session_priv }); } else { // This came from the first upstream node // Bank error in our favor! Maybe we should tell the user this somehow??? - pending_htlc = PendingOutboundHTLC::OutboundRoute { route }; + pending_htlc = PendingOutboundHTLC::OutboundRoute { route, session_priv }; channel_state.claimable_htlcs.insert(payment_hash, PendingOutboundHTLC::IntermediaryHopData { source_short_channel_id, incoming_packet_shared_secret }); } }, @@ -1332,7 +1352,7 @@ impl ChannelMessageHandler for ChannelManager { let chan = channel_state.by_id.get_mut(&forwarding_id).unwrap(); if !chan.is_live() { let chan_update = self.get_channel_update(chan).unwrap(); - return_err!("Forwarding channel is not in a ready state.", 0x4000 | 10, &chan_update.encode()[..]); + return_err!("Forwarding channel is not in a ready state.", 0x4000 | 7, &chan_update.encode_with_len()[..]); } } @@ -1376,16 +1396,17 @@ impl ChannelMessageHandler for ChannelManager { match claimable_htlcs_entry { hash_map::Entry::Occupied(mut e) => { let outbound_route = e.get_mut(); - let route = match outbound_route { - &mut PendingOutboundHTLC::OutboundRoute { ref route } => { - route.clone() + let (route, session_priv) = match outbound_route { + &mut PendingOutboundHTLC::OutboundRoute { ref route, ref session_priv } => { + (route.clone(), session_priv.clone()) }, _ => { panic!("WAT") }, }; *outbound_route = PendingOutboundHTLC::CycledRoute { source_short_channel_id, incoming_packet_shared_secret: shared_secret, - route + route, + session_priv, }; }, hash_map::Entry::Vacant(e) => { @@ -1422,9 +1443,9 @@ impl ChannelMessageHandler for ChannelManager { Ok(()) } - fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &msgs::UpdateFailHTLC) -> Result<(), HandleError> { + fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &msgs::UpdateFailHTLC) -> Result, HandleError> { let mut channel_state = self.channel_state.lock().unwrap(); - match channel_state.by_id.get_mut(&msg.channel_id) { + let payment_hash = match channel_state.by_id.get_mut(&msg.channel_id) { Some(chan) => { if chan.get_their_node_id() != *their_node_id { return Err(HandleError{err: "Got a message for a channel from the wrong node!", msg: None}) @@ -1432,6 +1453,68 @@ impl ChannelMessageHandler for ChannelManager { chan.update_fail_htlc(&msg, HTLCFailReason::ErrorPacket { err: msg.reason.clone() }) }, None => return Err(HandleError{err: "Failed to find corresponding channel", msg: None}) + }?; + + if let Some(pending_htlc) = channel_state.claimable_htlcs.get(&payment_hash) { + match pending_htlc { + &PendingOutboundHTLC::OutboundRoute { ref route, ref session_priv } => { + // Handle packed channel/node updates for passing back for the route handler + let mut packet_decrypted = msg.reason.data.clone(); + let mut res = None; + Self::construct_onion_keys_callback(&self.secp_ctx, &route, &session_priv, |shared_secret, _, _, route_hop| { + if res.is_some() { return; } + + let ammag = ChannelManager::gen_ammag_from_shared_secret(&shared_secret); + + let mut decryption_tmp = Vec::with_capacity(packet_decrypted.len()); + decryption_tmp.resize(packet_decrypted.len(), 0); + let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]); + chacha.process(&packet_decrypted, &mut decryption_tmp[..]); + packet_decrypted = decryption_tmp; + + if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::decode(&packet_decrypted) { + if err_packet.failuremsg.len() >= 2 { + let um = ChannelManager::gen_um_from_shared_secret(&shared_secret); + + let mut hmac = Hmac::new(Sha256::new(), &um); + hmac.input(&err_packet.encode()[32..]); + let mut calc_tag = [0u8; 32]; + hmac.raw_result(&mut calc_tag); + if crypto::util::fixed_time_eq(&calc_tag, &err_packet.hmac) { + const UNKNOWN_CHAN: u16 = 0x4000|10; + const TEMP_CHAN_FAILURE: u16 = 0x4000|7; + match byte_utils::slice_to_be16(&err_packet.failuremsg[0..2]) { + TEMP_CHAN_FAILURE => { + if err_packet.failuremsg.len() >= 4 { + let update_len = byte_utils::slice_to_be16(&err_packet.failuremsg[2..4]) as usize; + if err_packet.failuremsg.len() >= 4 + update_len { + if let Ok(chan_update) = msgs::ChannelUpdate::decode(&err_packet.failuremsg[4..4 + update_len]) { + res = Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { + msg: chan_update, + }); + } + } + } + }, + UNKNOWN_CHAN => { + // No such next-hop. We know this came from the + // current node as the HMAC validated. + res = Some(msgs::HTLCFailChannelUpdate::ChannelClosed { + short_channel_id: route_hop.short_channel_id + }); + }, + _ => {}, //TODO: Enumerate all of these! + } + } + } + } + }).unwrap(); + Ok(res) + }, + _ => { Ok(None) }, + } + } else { + Ok(None) } } @@ -2169,14 +2252,7 @@ mod tests { claim_payment(&origin, expected_route, our_payment_preimage); } - fn send_failed_payment(origin_node: &Node, expected_route: &[&Node]) { - let route = origin_node.router.get_route(&expected_route.last().unwrap().node.get_our_node_id(), &Vec::new(), 1000000, 142).unwrap(); - assert_eq!(route.hops.len(), expected_route.len()); - for (node, hop) in expected_route.iter().zip(route.hops.iter()) { - assert_eq!(hop.pubkey, node.node.get_our_node_id()); - } - let our_payment_hash = send_along_route(origin_node, route, expected_route, 1000000).1; - + fn fail_payment(origin_node: &Node, expected_route: &[&Node], our_payment_hash: [u8; 32]) { assert!(expected_route.last().unwrap().node.fail_htlc_backwards(&our_payment_hash)); let mut next_msgs: Option<(msgs::UpdateFailHTLC, msgs::CommitmentSigned)> = None; @@ -2288,7 +2364,8 @@ mod tests { send_payment(&nodes[3], &vec!(&nodes[2], &nodes[1])[..], 1000000); // Test failure packets - send_failed_payment(&nodes[0], &vec!(&nodes[1], &nodes[2], &nodes[3])[..]); + let payment_hash_1 = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2], &nodes[3])[..], 1000000).1; + fail_payment(&nodes[0], &vec!(&nodes[1], &nodes[2], &nodes[3])[..], payment_hash_1); // Add a new channel that skips 3 let chan_4 = create_announced_chan_between_nodes(&nodes, 1, 3); @@ -2346,10 +2423,10 @@ mod tests { }); hops[1].fee_msat = chan_2.1.contents.fee_base_msat as u64 + chan_2.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000; hops[0].fee_msat = chan_3.1.contents.fee_base_msat as u64 + chan_3.1.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000; - let payment_preimage_2 = send_along_route(&nodes[1], Route { hops }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).0; + let payment_hash_2 = send_along_route(&nodes[1], Route { hops }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1; // Claim the rebalances... - claim_payment(&nodes[1], &vec!(&nodes[3], &nodes[2], &nodes[1])[..], payment_preimage_2); + fail_payment(&nodes[1], &vec!(&nodes[3], &nodes[2], &nodes[1])[..], payment_hash_2); claim_payment(&nodes[1], &vec!(&nodes[2], &nodes[3], &nodes[1])[..], payment_preimage_1); // Add a duplicate new channel from 2 to 4 diff --git a/src/ln/msgs.rs b/src/ln/msgs.rs index c48e05681..681817a9e 100644 --- a/src/ln/msgs.rs +++ b/src/ln/msgs.rs @@ -15,6 +15,14 @@ pub trait MsgEncodable { fn encode(&self) -> Vec; #[inline] fn encoded_len(&self) -> usize { self.encode().len() } + #[inline] + fn encode_with_len(&self) -> Vec { + let enc = self.encode(); + let mut res = Vec::with_capacity(enc.len() + 2); + res.extend_from_slice(&byte_utils::be16_to_array(enc.len() as u16)); + res.extend_from_slice(&enc); + res + } } #[derive(Debug)] pub enum DecodeError { @@ -366,6 +374,15 @@ pub struct CommitmentUpdate { pub commitment_signed: CommitmentSigned, } +pub enum HTLCFailChannelUpdate { + ChannelUpdateMessage { + msg: ChannelUpdate, + }, + ChannelClosed { + short_channel_id: u64, + }, +} + /// A trait to describe an object which can receive channel messages. Messages MAY be called in /// paralell when they originate from different their_node_ids, however they MUST NOT be called in /// paralell when the two calls have the same their_node_id. @@ -384,7 +401,7 @@ pub trait ChannelMessageHandler : events::EventsProvider { // HTLC handling: fn handle_update_add_htlc(&self, their_node_id: &PublicKey, msg: &UpdateAddHTLC) -> Result<(), HandleError>; fn handle_update_fulfill_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFulfillHTLC) -> Result<(), HandleError>; - fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result<(), HandleError>; + fn handle_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailHTLC) -> Result, HandleError>; fn handle_update_fail_malformed_htlc(&self, their_node_id: &PublicKey, msg: &UpdateFailMalformedHTLC) -> Result<(), HandleError>; fn handle_commitment_signed(&self, their_node_id: &PublicKey, msg: &CommitmentSigned) -> Result<(RevokeAndACK, Option), HandleError>; fn handle_revoke_and_ack(&self, their_node_id: &PublicKey, msg: &RevokeAndACK) -> Result, HandleError>; @@ -408,6 +425,7 @@ pub trait RoutingMessageHandler { /// or returning an Err otherwise. fn handle_channel_announcement(&self, msg: &ChannelAnnouncement) -> Result; fn handle_channel_update(&self, msg: &ChannelUpdate) -> Result<(), HandleError>; + fn handle_htlc_fail_channel_update(&self, update: &HTLCFailChannelUpdate); } pub struct OnionRealm0HopData { @@ -1316,8 +1334,26 @@ impl MsgEncodable for OnionPacket { } impl MsgDecodable for DecodedOnionErrorPacket { - fn decode(_v: &[u8]) -> Result { - unimplemented!(); + fn decode(v: &[u8]) -> Result { + if v.len() < 32 + 4 { + return Err(DecodeError::WrongLength); + } + let failuremsg_len = byte_utils::slice_to_be16(&v[32..34]) as usize; + if v.len() < 32 + 4 + failuremsg_len { + return Err(DecodeError::WrongLength); + } + let padding_len = byte_utils::slice_to_be16(&v[34 + failuremsg_len..]) as usize; + if v.len() < 32 + 4 + failuremsg_len + padding_len { + return Err(DecodeError::WrongLength); + } + + let mut hmac = [0; 32]; + hmac.copy_from_slice(&v[0..32]); + Ok(Self { + hmac, + failuremsg: v[34..34 + failuremsg_len].to_vec(), + pad: v[36 + failuremsg_len..36 + failuremsg_len + padding_len].to_vec(), + }) } } impl MsgEncodable for DecodedOnionErrorPacket { diff --git a/src/ln/peer_handler.rs b/src/ln/peer_handler.rs index d7b1e9d69..624c64949 100644 --- a/src/ln/peer_handler.rs +++ b/src/ln/peer_handler.rs @@ -424,7 +424,10 @@ impl PeerManager { }, 131 => { let msg = try_potential_decodeerror!(msgs::UpdateFailHTLC::decode(&msg_data[2..])); - try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg)); + let chan_update = try_potential_handleerror!(self.message_handler.chan_handler.handle_update_fail_htlc(&peer.their_node_id.unwrap(), &msg)); + if let Some(update) = chan_update { + self.message_handler.route_handler.handle_htlc_fail_channel_update(&update); + } }, 135 => { let msg = try_potential_decodeerror!(msgs::UpdateFailMalformedHTLC::decode(&msg_data[2..])); diff --git a/src/ln/router.rs b/src/ln/router.rs index 75df79450..19a677580 100644 --- a/src/ln/router.rs +++ b/src/ln/router.rs @@ -51,9 +51,9 @@ struct ChannelInfo { struct NodeInfo { #[cfg(feature = "non_bitcoin_chain_hash_routing")] - channels: Vec<(u64, Sha256dHash)>, + channels: Vec<(u64, Sha256dHash)>, #[cfg(not(feature = "non_bitcoin_chain_hash_routing"))] - channels: Vec, + channels: Vec, lowest_inbound_channel_fee_base_msat: u32, lowest_inbound_channel_fee_proportional_millionths: u32, @@ -214,6 +214,18 @@ impl RoutingMessageHandler for Router { Ok(!msg.contents.features.supports_unknown_bits()) } + fn handle_htlc_fail_channel_update(&self, update: &msgs::HTLCFailChannelUpdate) { + match update { + &msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } => { + let _ = self.handle_channel_update(msg); + }, + &msgs::HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id } => { + let mut network = self.network_map.write().unwrap(); + network.channels.remove(short_channel_id); + }, + } + } + fn handle_channel_update(&self, msg: &msgs::ChannelUpdate) -> Result<(), HandleError> { let mut network = self.network_map.write().unwrap(); let dest_node_id;