Add maybe_handle_error_without_close for OutboundV2Channel
[rust-lightning] / lightning / src / ln / onion_utils.rs
index 31f2f7827bcb57c42d7b2a625a842abdaa00ab17..4b39276c066c77ec5975be569c274081158750ed 100644 (file)
@@ -14,7 +14,8 @@ use crate::ln::wire::Encode;
 use crate::routing::gossip::NetworkUpdate;
 use crate::routing::router::{BlindedTail, Path, RouteHop};
 use crate::sign::NodeSigner;
-use crate::util::chacha20::{ChaCha20, ChaChaReader};
+use crate::crypto::chacha20::ChaCha20;
+use crate::crypto::streams::ChaChaReader;
 use crate::util::errors::{self, APIError};
 use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, LengthCalculatingWriter};
 use crate::util::logger::Logger;
@@ -188,11 +189,10 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
                                for (i, blinded_hop) in hops.iter().enumerate() {
                                        if i == hops.len() - 1 {
                                                cur_value_msat += final_value_msat;
-                                               cur_cltv += excess_final_cltv_expiry_delta;
                                                res.push(msgs::OutboundOnionPayload::BlindedReceive {
-                                                       amt_msat: *final_value_msat,
+                                                       sender_intended_htlc_amt_msat: *final_value_msat,
                                                        total_msat,
-                                                       outgoing_cltv_value: cltv,
+                                                       cltv_expiry_height: cur_cltv + excess_final_cltv_expiry_delta,
                                                        encrypted_tlvs: blinded_hop.encrypted_payload.clone(),
                                                        intro_node_blinding_point: blinding_point.take(),
                                                });
@@ -214,8 +214,8 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
                                        payment_metadata: recipient_onion.payment_metadata.take(),
                                        keysend_preimage: *keysend_preimage,
                                        custom_tlvs: recipient_onion.custom_tlvs.clone(),
-                                       amt_msat: value_msat,
-                                       outgoing_cltv_value: cltv,
+                                       sender_intended_htlc_amt_msat: value_msat,
+                                       cltv_expiry_height: cltv,
                                });
                        }
                } else {
@@ -242,6 +242,8 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
 /// the hops can be of variable length.
 pub(crate) const ONION_DATA_LEN: usize = 20*65;
 
+pub(super) const INVALID_ONION_BLINDING: u16 = 0x8000 | 0x4000 | 24;
+
 #[inline]
 fn shift_slice_right(arr: &mut [u8], amt: usize) {
        for i in (amt..arr.len()).rev() {
@@ -321,8 +323,6 @@ fn construct_onion_packet_with_init_noise<HD: Writeable, P: Packet>(
 
                let mut pos = 0;
                for (i, (payload, keys)) in payloads.iter().zip(onion_keys.iter()).enumerate() {
-                       if i == payloads.len() - 1 { break; }
-
                        let mut chacha = ChaCha20::new(&keys.rho, &[0u8; 8]);
                        for _ in 0..(packet_data.len() - pos) { // TODO: Batch this.
                                let mut dummy = [0; 1];
@@ -336,6 +336,8 @@ fn construct_onion_packet_with_init_noise<HD: Writeable, P: Packet>(
                                return Err(());
                        }
 
+                       if i == payloads.len() - 1 { break; }
+
                        res.resize(pos, 0u8);
                        chacha.process_in_place(&mut res);
                }
@@ -427,17 +429,29 @@ pub(crate) struct DecodedOnionFailure {
        pub(crate) network_update: Option<NetworkUpdate>,
        pub(crate) short_channel_id: Option<u64>,
        pub(crate) payment_failed_permanently: bool,
+       pub(crate) failed_within_blinded_path: bool,
        #[cfg(test)]
        pub(crate) onion_error_code: Option<u16>,
        #[cfg(test)]
        pub(crate) onion_error_data: Option<Vec<u8>>,
 }
 
+/// Note that we always decrypt `packet` in-place here even if the deserialization into
+/// [`msgs::DecodedOnionErrorPacket`] ultimately fails.
+fn decrypt_onion_error_packet(
+       packet: &mut Vec<u8>, shared_secret: SharedSecret
+) -> Result<msgs::DecodedOnionErrorPacket, msgs::DecodeError> {
+       let ammag = gen_ammag_from_shared_secret(shared_secret.as_ref());
+       let mut chacha = ChaCha20::new(&ammag, &[0u8; 8]);
+       chacha.process_in_place(packet);
+       msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(packet))
+}
+
 /// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
 /// OutboundRoute).
 #[inline]
 pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
-       secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>
+       secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource, mut encrypted_packet: Vec<u8>
 ) -> DecodedOnionFailure where L::Target: Logger {
        let (path, session_priv, first_hop_htlc_msat) = if let &HTLCSource::OutboundRoute {
                ref path, ref session_priv, ref first_hop_htlc_msat, ..
@@ -450,6 +464,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
                network_update: Option<NetworkUpdate>,
                short_channel_id: Option<u64>,
                payment_failed_permanently: bool,
+               failed_within_blinded_path: bool,
        }
        let mut res: Option<FailureLearnings> = None;
        let mut htlc_msat = *first_hop_htlc_msat;
@@ -475,7 +490,8 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
                                error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding
                                error_packet_ret = Some(vec![0; 32]);
                                res = Some(FailureLearnings {
-                                       network_update: None, short_channel_id: None, payment_failed_permanently: false
+                                       network_update: None, short_channel_id: None, payment_failed_permanently: false,
+                                       failed_within_blinded_path: true,
                                });
                                return
                        },
@@ -491,10 +507,24 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
                                Some(hop) => hop,
                                None => {
                                        // The failing hop is within a multi-hop blinded path.
-                                       error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding
-                                       error_packet_ret = Some(vec![0; 32]);
+                                       #[cfg(not(test))] {
+                                               error_code_ret = Some(BADONION | PERM | 24); // invalid_onion_blinding
+                                               error_packet_ret = Some(vec![0; 32]);
+                                       }
+                                       #[cfg(test)] {
+                                               // Actually parse the onion error data in tests so we can check that blinded hops fail
+                                               // back correctly.
+                                               let err_packet = decrypt_onion_error_packet(
+                                                       &mut encrypted_packet, shared_secret
+                                               ).unwrap();
+                                               error_code_ret =
+                                                       Some(u16::from_be_bytes(err_packet.failuremsg.get(0..2).unwrap().try_into().unwrap()));
+                                               error_packet_ret = Some(err_packet.failuremsg[2..].to_vec());
+                                       }
+
                                        res = Some(FailureLearnings {
-                                               network_update: None, short_channel_id: None, payment_failed_permanently: false
+                                               network_update: None, short_channel_id: None, payment_failed_permanently: false,
+                                               failed_within_blinded_path: true,
                                        });
                                        return
                                }
@@ -504,15 +534,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
                let amt_to_forward = htlc_msat - route_hop.fee_msat;
                htlc_msat = amt_to_forward;
 
-               let ammag = gen_ammag_from_shared_secret(shared_secret.as_ref());
-
-               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;
-
-               let err_packet = match msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
+               let err_packet = match decrypt_onion_error_packet(&mut encrypted_packet, shared_secret) {
                        Ok(p) => p,
                        Err(_) => return
                };
@@ -532,7 +554,8 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
                                });
                                let short_channel_id = Some(route_hop.short_channel_id);
                                res = Some(FailureLearnings {
-                                       network_update, short_channel_id, payment_failed_permanently: is_from_final_node
+                                       network_update, short_channel_id, payment_failed_permanently: is_from_final_node,
+                                       failed_within_blinded_path: false
                                });
                                return
                        }
@@ -688,7 +711,8 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
 
                res = Some(FailureLearnings {
                        network_update, short_channel_id,
-                       payment_failed_permanently: error_code & PERM == PERM && is_from_final_node
+                       payment_failed_permanently: error_code & PERM == PERM && is_from_final_node,
+                       failed_within_blinded_path: false
                });
 
                let (description, title) = errors::get_onion_error_description(error_code);
@@ -699,10 +723,10 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
                }
        }).expect("Route that we sent via spontaneously grew invalid keys in the middle of it?");
        if let Some(FailureLearnings {
-               network_update, short_channel_id, payment_failed_permanently
+               network_update, short_channel_id, payment_failed_permanently, failed_within_blinded_path
        }) = res {
                DecodedOnionFailure {
-                       network_update, short_channel_id, payment_failed_permanently,
+                       network_update, short_channel_id, payment_failed_permanently, failed_within_blinded_path,
                        #[cfg(test)]
                        onion_error_code: error_code_ret,
                        #[cfg(test)]
@@ -713,6 +737,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
                // payment not retryable only when garbage is from the final node
                DecodedOnionFailure {
                        network_update: None, short_channel_id: None, payment_failed_permanently: is_from_final_node,
+                       failed_within_blinded_path: false,
                        #[cfg(test)]
                        onion_error_code: None,
                        #[cfg(test)]
@@ -722,9 +747,11 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(
 }
 
 #[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
+#[cfg_attr(test, derive(PartialEq))]
 pub(super) struct HTLCFailReason(HTLCFailReasonRepr);
 
 #[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
+#[cfg_attr(test, derive(PartialEq))]
 enum HTLCFailReasonRepr {
        LightningError {
                err: msgs::OnionErrorPacket,
@@ -858,6 +885,7 @@ impl HTLCFailReason {
                                                network_update: None,
                                                payment_failed_permanently: false,
                                                short_channel_id: Some(path.hops[0].short_channel_id),
+                                               failed_within_blinded_path: false,
                                                #[cfg(test)]
                                                onion_error_code: Some(*failure_code),
                                                #[cfg(test)]
@@ -920,9 +948,11 @@ pub(crate) enum OnionDecodeErr {
 
 pub(crate) fn decode_next_payment_hop<NS: Deref>(
        shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash,
-       node_signer: &NS,
+       blinding_point: Option<PublicKey>, node_signer: &NS,
 ) -> Result<Hop, OnionDecodeErr> where NS::Target: NodeSigner {
-       match decode_next_hop(shared_secret, hop_data, hmac_bytes, Some(payment_hash), node_signer) {
+       match decode_next_hop(
+               shared_secret, hop_data, hmac_bytes, Some(payment_hash), (blinding_point, node_signer)
+       ) {
                Ok((next_hop_data, None)) => Ok(Hop::Receive(next_hop_data)),
                Ok((next_hop_data, Some((next_hop_hmac, FixedSizeOnionPacket(new_packet_bytes))))) => {
                        Ok(Hop::Forward {
@@ -1001,18 +1031,20 @@ fn decode_next_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(shared_secret: [u8
                        if hmac == [0; 32] {
                                #[cfg(test)]
                                {
-                                       // In tests, make sure that the initial onion packet data is, at least, non-0.
-                                       // We could do some fancy randomness test here, but, ehh, whatever.
-                                       // This checks for the issue where you can calculate the path length given the
-                                       // onion data as all the path entries that the originator sent will be here
-                                       // as-is (and were originally 0s).
-                                       // Of course reverse path calculation is still pretty easy given naive routing
-                                       // algorithms, but this fixes the most-obvious case.
-                                       let mut next_bytes = [0; 32];
-                                       chacha_stream.read_exact(&mut next_bytes).unwrap();
-                                       assert_ne!(next_bytes[..], [0; 32][..]);
-                                       chacha_stream.read_exact(&mut next_bytes).unwrap();
-                                       assert_ne!(next_bytes[..], [0; 32][..]);
+                                       if chacha_stream.read.position() < hop_data.len() as u64 - 64 {
+                                               // In tests, make sure that the initial onion packet data is, at least, non-0.
+                                               // We could do some fancy randomness test here, but, ehh, whatever.
+                                               // This checks for the issue where you can calculate the path length given the
+                                               // onion data as all the path entries that the originator sent will be here
+                                               // as-is (and were originally 0s).
+                                               // Of course reverse path calculation is still pretty easy given naive routing
+                                               // algorithms, but this fixes the most-obvious case.
+                                               let mut next_bytes = [0; 32];
+                                               chacha_stream.read_exact(&mut next_bytes).unwrap();
+                                               assert_ne!(next_bytes[..], [0; 32][..]);
+                                               chacha_stream.read_exact(&mut next_bytes).unwrap();
+                                               assert_ne!(next_bytes[..], [0; 32][..]);
+                                       }
                                }
                                return Ok((msg, None)); // We are the final destination for this packet
                        } else {