Move process_onion_failure into onion_utils
authorMatt Corallo <git@bluematt.me>
Thu, 20 Dec 2018 19:51:18 +0000 (14:51 -0500)
committerMatt Corallo <git@bluematt.me>
Fri, 21 Dec 2018 03:56:32 +0000 (22:56 -0500)
src/ln/channelmanager.rs
src/ln/onion_utils.rs

index b5ee9b562aff2eff2a5b31b3feef8189c0fe3e63..03d7e0866f1de10208b5ef04ac39f4d52f09f6f9 100644 (file)
@@ -39,7 +39,6 @@ use util::ser::{Readable, ReadableArgs, Writeable, Writer};
 use util::chacha20::ChaCha20;
 use util::logger::Logger;
 use util::errors::APIError;
-use util::errors;
 
 use std::{cmp, mem};
 use std::collections::{HashMap, hash_map, HashSet};
@@ -1297,9 +1296,9 @@ impl ChannelManager {
                                match &onion_error {
                                        &HTLCFailReason::ErrorPacket { ref err } => {
 #[cfg(test)]
-                                               let (channel_update, payment_retryable, onion_error_code) = self.process_onion_failure(&source, err.data.clone());
+                                               let (channel_update, payment_retryable, onion_error_code) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
 #[cfg(not(test))]
-                                               let (channel_update, payment_retryable, _) = self.process_onion_failure(&source, err.data.clone());
+                                               let (channel_update, payment_retryable, _) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
                                                // TODO: If we decided to blame ourselves (or one of our channels) in
                                                // process_onion_failure we should close that channel as it implies our
                                                // next-hop is needlessly blaming us!
@@ -1865,155 +1864,6 @@ impl ChannelManager {
                Ok(())
        }
 
-       // Process failure we got back from upstream on a payment we sent. Returns update and a boolean
-       // indicating that the payment itself failed
-       fn process_onion_failure(&self, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>) -> (Option<msgs::HTLCFailChannelUpdate>, bool, Option<u16>) {
-               if let &HTLCSource::OutboundRoute { ref route, ref session_priv, ref first_hop_htlc_msat } = htlc_source {
-
-                       let mut res = None;
-                       let mut htlc_msat = *first_hop_htlc_msat;
-                       let mut error_code_ret = None;
-                       let mut next_route_hop_ix = 0;
-                       let mut is_from_final_node = false;
-
-                       // Handle packed channel/node updates for passing back for the route handler
-                       onion_utils::construct_onion_keys_callback(&self.secp_ctx, route, session_priv, |shared_secret, _, _, route_hop| {
-                               next_route_hop_ix += 1;
-                               if res.is_some() { return; }
-
-                               let amt_to_forward = htlc_msat - route_hop.fee_msat;
-                               htlc_msat = amt_to_forward;
-
-                               let ammag = onion_utils::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;
-
-                               is_from_final_node = route.hops.last().unwrap().pubkey == route_hop.pubkey;
-
-                               if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
-                                       let um = onion_utils::gen_um_from_shared_secret(&shared_secret[..]);
-                                       let mut hmac = HmacEngine::<Sha256>::new(&um);
-                                       hmac.input(&err_packet.encode()[32..]);
-
-                                       if fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &err_packet.hmac) {
-                                               if let Some(error_code_slice) = err_packet.failuremsg.get(0..2) {
-                                                       const PERM: u16 = 0x4000;
-                                                       const NODE: u16 = 0x2000;
-                                                       const UPDATE: u16 = 0x1000;
-
-                                                       let error_code = byte_utils::slice_to_be16(&error_code_slice);
-                                                       error_code_ret = Some(error_code);
-
-                                                       let (debug_field, debug_field_size) = errors::get_onion_debug_field(error_code);
-
-                                                       // indicate that payment parameter has failed and no need to
-                                                       // update Route object
-                                                       let payment_failed = (match error_code & 0xff {
-                                                               15|16|17|18|19 => true,
-                                                               _ => false,
-                                                       } && is_from_final_node) // PERM bit observed below even this error is from the intermediate nodes
-                                                       || error_code == 21; // Special case error 21 as the Route object is bogus, TODO: Maybe fail the node if the CLTV was reasonable?
-
-                                                       let mut fail_channel_update = None;
-
-                                                       if error_code & NODE == NODE {
-                                                               fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent: error_code & PERM == PERM });
-                                                       }
-                                                       else if error_code & PERM == PERM {
-                                                               fail_channel_update = if payment_failed {None} else {Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
-                                                                       short_channel_id: route.hops[next_route_hop_ix - if next_route_hop_ix == route.hops.len() { 1 } else { 0 }].short_channel_id,
-                                                                       is_permanent: true,
-                                                               })};
-                                                       }
-                                                       else if error_code & UPDATE == UPDATE {
-                                                               if let Some(update_len_slice) = err_packet.failuremsg.get(debug_field_size+2..debug_field_size+4) {
-                                                                       let update_len = byte_utils::slice_to_be16(&update_len_slice) as usize;
-                                                                       if let Some(update_slice) = err_packet.failuremsg.get(debug_field_size + 4..debug_field_size + 4 + update_len) {
-                                                                               if let Ok(chan_update) = msgs::ChannelUpdate::read(&mut Cursor::new(&update_slice)) {
-                                                                                       // if channel_update should NOT have caused the failure:
-                                                                                       // MAY treat the channel_update as invalid.
-                                                                                       let is_chan_update_invalid = match error_code & 0xff {
-                                                                                               7 => false,
-                                                                                               11 => amt_to_forward > chan_update.contents.htlc_minimum_msat,
-                                                                                               12 => {
-                                                                                                       let new_fee = amt_to_forward.checked_mul(chan_update.contents.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan_update.contents.fee_base_msat as u64) });
-                                                                                                       new_fee.is_some() && route_hop.fee_msat >= new_fee.unwrap()
-                                                                                               }
-                                                                                               13 => route_hop.cltv_expiry_delta as u16 >= chan_update.contents.cltv_expiry_delta,
-                                                                                               14 => false, // expiry_too_soon; always valid?
-                                                                                               20 => chan_update.contents.flags & 2 == 0,
-                                                                                               _ => false, // unknown error code; take channel_update as valid
-                                                                                       };
-                                                                                       fail_channel_update = if is_chan_update_invalid {
-                                                                                               // This probably indicates the node which forwarded
-                                                                                               // to the node in question corrupted something.
-                                                                                               Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
-                                                                                                       short_channel_id: route_hop.short_channel_id,
-                                                                                                       is_permanent: true,
-                                                                                               })
-                                                                                       } else {
-                                                                                               Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {
-                                                                                                       msg: chan_update,
-                                                                                               })
-                                                                                       };
-                                                                               }
-                                                                       }
-                                                               }
-                                                               if fail_channel_update.is_none() {
-                                                                       // They provided an UPDATE which was obviously bogus, not worth
-                                                                       // trying to relay through them anymore.
-                                                                       fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure {
-                                                                               node_id: route_hop.pubkey,
-                                                                               is_permanent: true,
-                                                                       });
-                                                               }
-                                                       } else if !payment_failed {
-                                                               // We can't understand their error messages and they failed to
-                                                               // forward...they probably can't understand our forwards so its
-                                                               // really not worth trying any further.
-                                                               fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure {
-                                                                       node_id: route_hop.pubkey,
-                                                                       is_permanent: true,
-                                                               });
-                                                       }
-
-                                                       // TODO: Here (and a few other places) we assume that BADONION errors
-                                                       // are always "sourced" from the node previous to the one which failed
-                                                       // to decode the onion.
-                                                       res = Some((fail_channel_update, !(error_code & PERM == PERM && is_from_final_node)));
-
-                                                       let (description, title) = errors::get_onion_error_description(error_code);
-                                                       if debug_field_size > 0 && err_packet.failuremsg.len() >= 4 + debug_field_size {
-                                                               log_warn!(self, "Onion Error[{}({:#x}) {}({})] {}", title, error_code, debug_field, log_bytes!(&err_packet.failuremsg[4..4+debug_field_size]), description);
-                                                       }
-                                                       else {
-                                                               log_warn!(self, "Onion Error[{}({:#x})] {}", title, error_code, description);
-                                                       }
-                                               } else {
-                                                       // Useless packet that we can't use but it passed HMAC, so it
-                                                       // definitely came from the peer in question
-                                                       res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
-                                                               node_id: route_hop.pubkey,
-                                                               is_permanent: true,
-                                                       }), !is_from_final_node));
-                                               }
-                                       }
-                               }
-                       }).expect("Route that we sent via spontaneously grew invalid keys in the middle of it?");
-                       if let Some((channel_update, payment_retryable)) = res {
-                               (channel_update, payment_retryable, error_code_ret)
-                       } else {
-                               // only not set either packet unparseable or hmac does not match with any
-                               // payment not retryable only when garbage is from the final node
-                               (None, !is_from_final_node, None)
-                       }
-               } else { unreachable!(); }
-       }
-
        fn internal_update_fail_htlc(&self, their_node_id: &PublicKey, msg: &msgs::UpdateFailHTLC) -> Result<(), MsgHandleErrInternal> {
                let mut channel_lock = self.channel_state.lock().unwrap();
                let channel_state = channel_lock.borrow_parts();
index 85f98ffdd78b52ccea4c5c7b5db79412790f2af0..97ec0ed9b16be65f71d3f4f7f90ea176f6db18c2 100644 (file)
@@ -1,12 +1,14 @@
-use ln::channelmanager::PaymentHash;
+use ln::channelmanager::{PaymentHash, HTLCSource};
 use ln::msgs;
 use ln::router::{Route,RouteHop};
-use util::internal_traits;
+use util::{byte_utils, internal_traits};
 use util::chacha20::ChaCha20;
-use util::errors::APIError;
-use util::ser::Writeable;
+use util::errors::{self, APIError};
+use util::ser::{Readable, Writeable};
+use util::logger::Logger;
 
 use bitcoin_hashes::{Hash, HashEngine};
+use bitcoin_hashes::cmp::fixed_time_eq;
 use bitcoin_hashes::hmac::{Hmac, HmacEngine};
 use bitcoin_hashes::sha256::Hash as Sha256;
 
@@ -16,6 +18,8 @@ use secp256k1::ecdh::SharedSecret;
 use secp256k1;
 
 use std::ptr;
+use std::io::Cursor;
+use std::sync::Arc;
 
 pub(super) struct OnionKeys {
        #[cfg(test)]
@@ -261,6 +265,158 @@ pub(super) fn build_first_hop_failure_packet(shared_secret: &[u8], failure_type:
        encrypt_failure_packet(shared_secret, &failure_packet.encode()[..])
 }
 
+struct LogHolder<'a> { logger: &'a Arc<Logger> }
+/// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
+/// OutboundRoute).
+/// Returns update, a boolean indicating that the payment itself failed, and the error code.
+pub(super) fn process_onion_failure<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, logger: &Arc<Logger>, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>) -> (Option<msgs::HTLCFailChannelUpdate>, bool, Option<u16>) {
+       if let &HTLCSource::OutboundRoute { ref route, ref session_priv, ref first_hop_htlc_msat } = htlc_source {
+               let mut res = None;
+               let mut htlc_msat = *first_hop_htlc_msat;
+               let mut error_code_ret = None;
+               let mut next_route_hop_ix = 0;
+               let mut is_from_final_node = false;
+
+               // Handle packed channel/node updates for passing back for the route handler
+               construct_onion_keys_callback(secp_ctx, route, session_priv, |shared_secret, _, _, route_hop| {
+                       next_route_hop_ix += 1;
+                       if res.is_some() { return; }
+
+                       let amt_to_forward = htlc_msat - route_hop.fee_msat;
+                       htlc_msat = amt_to_forward;
+
+                       let ammag = 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;
+
+                       is_from_final_node = route.hops.last().unwrap().pubkey == route_hop.pubkey;
+
+                       if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
+                               let um = gen_um_from_shared_secret(&shared_secret[..]);
+                               let mut hmac = HmacEngine::<Sha256>::new(&um);
+                               hmac.input(&err_packet.encode()[32..]);
+
+                               if fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &err_packet.hmac) {
+                                       if let Some(error_code_slice) = err_packet.failuremsg.get(0..2) {
+                                               const PERM: u16 = 0x4000;
+                                               const NODE: u16 = 0x2000;
+                                               const UPDATE: u16 = 0x1000;
+
+                                               let error_code = byte_utils::slice_to_be16(&error_code_slice);
+                                               error_code_ret = Some(error_code);
+
+                                               let (debug_field, debug_field_size) = errors::get_onion_debug_field(error_code);
+
+                                               // indicate that payment parameter has failed and no need to
+                                               // update Route object
+                                               let payment_failed = (match error_code & 0xff {
+                                                       15|16|17|18|19 => true,
+                                                       _ => false,
+                                               } && is_from_final_node) // PERM bit observed below even this error is from the intermediate nodes
+                                               || error_code == 21; // Special case error 21 as the Route object is bogus, TODO: Maybe fail the node if the CLTV was reasonable?
+
+                                               let mut fail_channel_update = None;
+
+                                               if error_code & NODE == NODE {
+                                                       fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent: error_code & PERM == PERM });
+                                               }
+                                               else if error_code & PERM == PERM {
+                                                       fail_channel_update = if payment_failed {None} else {Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
+                                                               short_channel_id: route.hops[next_route_hop_ix - if next_route_hop_ix == route.hops.len() { 1 } else { 0 }].short_channel_id,
+                                                               is_permanent: true,
+                                                       })};
+                                               }
+                                               else if error_code & UPDATE == UPDATE {
+                                                       if let Some(update_len_slice) = err_packet.failuremsg.get(debug_field_size+2..debug_field_size+4) {
+                                                               let update_len = byte_utils::slice_to_be16(&update_len_slice) as usize;
+                                                               if let Some(update_slice) = err_packet.failuremsg.get(debug_field_size + 4..debug_field_size + 4 + update_len) {
+                                                                       if let Ok(chan_update) = msgs::ChannelUpdate::read(&mut Cursor::new(&update_slice)) {
+                                                                               // if channel_update should NOT have caused the failure:
+                                                                               // MAY treat the channel_update as invalid.
+                                                                               let is_chan_update_invalid = match error_code & 0xff {
+                                                                                       7 => false,
+                                                                                       11 => amt_to_forward > chan_update.contents.htlc_minimum_msat,
+                                                                                       12 => {
+                                                                                               let new_fee = amt_to_forward.checked_mul(chan_update.contents.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan_update.contents.fee_base_msat as u64) });
+                                                                                               new_fee.is_some() && route_hop.fee_msat >= new_fee.unwrap()
+                                                                                       }
+                                                                                       13 => route_hop.cltv_expiry_delta as u16 >= chan_update.contents.cltv_expiry_delta,
+                                                                                       14 => false, // expiry_too_soon; always valid?
+                                                                                       20 => chan_update.contents.flags & 2 == 0,
+                                                                                       _ => false, // unknown error code; take channel_update as valid
+                                                                               };
+                                                                               fail_channel_update = if is_chan_update_invalid {
+                                                                                       // This probably indicates the node which forwarded
+                                                                                       // to the node in question corrupted something.
+                                                                                       Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
+                                                                                               short_channel_id: route_hop.short_channel_id,
+                                                                                               is_permanent: true,
+                                                                                       })
+                                                                               } else {
+                                                                                       Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {
+                                                                                               msg: chan_update,
+                                                                                       })
+                                                                               };
+                                                                       }
+                                                               }
+                                                       }
+                                                       if fail_channel_update.is_none() {
+                                                               // They provided an UPDATE which was obviously bogus, not worth
+                                                               // trying to relay through them anymore.
+                                                               fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure {
+                                                                       node_id: route_hop.pubkey,
+                                                                       is_permanent: true,
+                                                               });
+                                                       }
+                                               } else if !payment_failed {
+                                                       // We can't understand their error messages and they failed to
+                                                       // forward...they probably can't understand our forwards so its
+                                                       // really not worth trying any further.
+                                                       fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure {
+                                                               node_id: route_hop.pubkey,
+                                                               is_permanent: true,
+                                                       });
+                                               }
+
+                                               // TODO: Here (and a few other places) we assume that BADONION errors
+                                               // are always "sourced" from the node previous to the one which failed
+                                               // to decode the onion.
+                                               res = Some((fail_channel_update, !(error_code & PERM == PERM && is_from_final_node)));
+
+                                               let (description, title) = errors::get_onion_error_description(error_code);
+                                               if debug_field_size > 0 && err_packet.failuremsg.len() >= 4 + debug_field_size {
+                                                       let log_holder = LogHolder { logger };
+                                                       log_warn!(log_holder, "Onion Error[{}({:#x}) {}({})] {}", title, error_code, debug_field, log_bytes!(&err_packet.failuremsg[4..4+debug_field_size]), description);
+                                               }
+                                               else {
+                                                       let log_holder = LogHolder { logger };
+                                                       log_warn!(log_holder, "Onion Error[{}({:#x})] {}", title, error_code, description);
+                                               }
+                                       } else {
+                                               // Useless packet that we can't use but it passed HMAC, so it
+                                               // definitely came from the peer in question
+                                               res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
+                                                       node_id: route_hop.pubkey,
+                                                       is_permanent: true,
+                                               }), !is_from_final_node));
+                                       }
+                               }
+                       }
+               }).expect("Route that we sent via spontaneously grew invalid keys in the middle of it?");
+               if let Some((channel_update, payment_retryable)) = res {
+                       (channel_update, payment_retryable, error_code_ret)
+               } else {
+                       // only not set either packet unparseable or hmac does not match with any
+                       // payment not retryable only when garbage is from the final node
+                       (None, !is_from_final_node, None)
+               }
+       } else { unreachable!(); }
+}
+
 #[cfg(test)]
 mod tests {
        use ln::channelmanager::PaymentHash;