Merge pull request #1761 from TheBlueMatt/2022-10-user-idempotency-token
[rust-lightning] / lightning / src / ln / onion_utils.rs
index 3795ad5ee77d70a03e5124fc3800823f610e0634..953313955a50220ed9b7d9693897facc35fbf7e6 100644 (file)
@@ -7,16 +7,16 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
-use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
-use ln::channelmanager::HTLCSource;
-use ln::msgs;
-use ln::wire::Encode;
-use routing::gossip::NetworkUpdate;
-use routing::router::RouteHop;
-use util::chacha20::{ChaCha20, ChaChaReader};
-use util::errors::{self, APIError};
-use util::ser::{Readable, ReadableArgs, Writeable, LengthCalculatingWriter};
-use util::logger::Logger;
+use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
+use crate::ln::channelmanager::HTLCSource;
+use crate::ln::msgs;
+use crate::ln::wire::Encode;
+use crate::routing::gossip::NetworkUpdate;
+use crate::routing::router::RouteHop;
+use crate::util::chacha20::{ChaCha20, ChaChaReader};
+use crate::util::errors::{self, APIError};
+use crate::util::ser::{Readable, ReadableArgs, Writeable, LengthCalculatingWriter};
+use crate::util::logger::Logger;
 
 use bitcoin::hashes::{Hash, HashEngine};
 use bitcoin::hashes::cmp::fixed_time_eq;
@@ -28,8 +28,8 @@ use bitcoin::secp256k1::Secp256k1;
 use bitcoin::secp256k1::ecdh::SharedSecret;
 use bitcoin::secp256k1;
 
-use prelude::*;
-use io::{Cursor, Read};
+use crate::prelude::*;
+use crate::io::{Cursor, Read};
 use core::convert::{AsMut, TryInto};
 use core::ops::Deref;
 
@@ -425,6 +425,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
 
                                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 BADONION: u16 = 0x8000;
                                                const PERM: u16 = 0x4000;
                                                const NODE: u16 = 0x2000;
                                                const UPDATE: u16 = 0x1000;
@@ -445,12 +446,24 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                                let mut network_update = None;
                                                let mut short_channel_id = None;
 
-                                               if error_code & NODE == NODE {
+                                               if error_code & BADONION == BADONION {
+                                                       // If the error code has the BADONION bit set, always blame the channel
+                                                       // from the node "originating" the error to its next hop. The
+                                                       // "originator" is ultimately actually claiming that its counterparty
+                                                       // is the one who is failing the HTLC.
+                                                       // If the "originator" here isn't lying we should really mark the
+                                                       // next-hop node as failed entirely, but we can't be confident in that,
+                                                       // as it would allow any node to get us to completely ban one of its
+                                                       // counterparties. Instead, we simply remove the channel in question.
+                                                       network_update = Some(NetworkUpdate::ChannelFailure {
+                                                               short_channel_id: failing_route_hop.short_channel_id,
+                                                               is_permanent: true,
+                                                       });
+                                               } else if error_code & NODE == NODE {
                                                        let is_permanent = error_code & PERM == PERM;
                                                        network_update = Some(NetworkUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent });
                                                        short_channel_id = Some(route_hop.short_channel_id);
-                                               }
-                                               else if error_code & PERM == PERM {
+                                               } else if error_code & PERM == PERM {
                                                        if !payment_failed {
                                                                network_update = Some(NetworkUpdate::ChannelFailure {
                                                                        short_channel_id: failing_route_hop.short_channel_id,
@@ -458,8 +471,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                                                });
                                                                short_channel_id = Some(failing_route_hop.short_channel_id);
                                                        }
-                                               }
-                                               else if error_code & UPDATE == UPDATE {
+                                               } 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 = u16::from_be_bytes(update_len_slice.try_into().expect("len is 2")) as usize;
                                                                if let Some(mut update_slice) = err_packet.failuremsg.get(debug_field_size + 4..debug_field_size + 4 + update_len) {
@@ -545,9 +557,6 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                                        short_channel_id = Some(route_hop.short_channel_id);
                                                }
 
-                                               // 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((network_update, short_channel_id, !(error_code & PERM == PERM && is_from_final_node)));
 
                                                let (description, title) = errors::get_onion_error_description(error_code);
@@ -580,31 +589,6 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
        } else { unreachable!(); }
 }
 
-/// An input used when decoding an onion packet.
-pub(crate) trait DecodeInput {
-       type Arg;
-       /// If Some, this is the input when checking the hmac of the onion packet.
-       fn payment_hash(&self) -> Option<&PaymentHash>;
-       /// Read argument when decrypting our hop payload.
-       fn read_arg(self) -> Self::Arg;
-}
-
-impl DecodeInput for PaymentHash {
-       type Arg = ();
-       fn payment_hash(&self) -> Option<&PaymentHash> {
-               Some(self)
-       }
-       fn read_arg(self) -> Self::Arg { () }
-}
-
-impl DecodeInput for SharedSecret {
-       type Arg = SharedSecret;
-       fn payment_hash(&self) -> Option<&PaymentHash> {
-               None
-       }
-       fn read_arg(self) -> Self::Arg { self }
-}
-
 /// Allows `decode_next_hop` to return the next hop packet bytes for either payments or onion
 /// message forwards.
 pub(crate) trait NextPacketBytes: AsMut<[u8]> {
@@ -655,7 +639,7 @@ pub(crate) enum OnionDecodeErr {
 }
 
 pub(crate) fn decode_next_payment_hop(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: PaymentHash) -> Result<Hop, OnionDecodeErr> {
-       match decode_next_hop(shared_secret, hop_data, hmac_bytes, payment_hash) {
+       match decode_next_hop(shared_secret, hop_data, hmac_bytes, Some(payment_hash), ()) {
                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 {
@@ -668,12 +652,16 @@ pub(crate) fn decode_next_payment_hop(shared_secret: [u8; 32], hop_data: &[u8],
        }
 }
 
-pub(crate) fn decode_next_hop<D: DecodeInput, R: ReadableArgs<D::Arg>, N: NextPacketBytes>(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], decode_input: D) -> Result<(R, Option<([u8; 32], N)>), OnionDecodeErr> {
+pub(crate) fn decode_next_untagged_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], read_args: T) -> Result<(R, Option<([u8; 32], N)>), OnionDecodeErr> {
+       decode_next_hop(shared_secret, hop_data, hmac_bytes, None, read_args)
+}
+
+fn decode_next_hop<T, R: ReadableArgs<T>, N: NextPacketBytes>(shared_secret: [u8; 32], hop_data: &[u8], hmac_bytes: [u8; 32], payment_hash: Option<PaymentHash>, read_args: T) -> Result<(R, Option<([u8; 32], N)>), OnionDecodeErr> {
        let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret);
        let mut hmac = HmacEngine::<Sha256>::new(&mu);
        hmac.input(hop_data);
-       if let Some(payment_hash) = decode_input.payment_hash() {
-               hmac.input(&payment_hash.0[..]);
+       if let Some(tag) = payment_hash {
+               hmac.input(&tag.0[..]);
        }
        if !fixed_time_eq(&Hmac::from_engine(hmac).into_inner(), &hmac_bytes) {
                return Err(OnionDecodeErr::Malformed {
@@ -684,7 +672,7 @@ pub(crate) fn decode_next_hop<D: DecodeInput, R: ReadableArgs<D::Arg>, N: NextPa
 
        let mut chacha = ChaCha20::new(&rho, &[0u8; 8]);
        let mut chacha_stream = ChaChaReader { chacha: &mut chacha, read: Cursor::new(&hop_data[..]) };
-       match R::read(&mut chacha_stream, decode_input.read_arg()) {
+       match R::read(&mut chacha_stream, read_args) {
                Err(err) => {
                        let error_code = match err {
                                msgs::DecodeError::UnknownVersion => 0x4000 | 1, // unknown realm byte
@@ -747,13 +735,13 @@ pub(crate) fn decode_next_hop<D: DecodeInput, R: ReadableArgs<D::Arg>, N: NextPa
 
 #[cfg(test)]
 mod tests {
-       use io;
-       use prelude::*;
-       use ln::PaymentHash;
-       use ln::features::{ChannelFeatures, NodeFeatures};
-       use routing::router::{Route, RouteHop};
-       use ln::msgs;
-       use util::ser::{Writeable, Writer};
+       use crate::io;
+       use crate::prelude::*;
+       use crate::ln::PaymentHash;
+       use crate::ln::features::{ChannelFeatures, NodeFeatures};
+       use crate::routing::router::{Route, RouteHop};
+       use crate::ln::msgs;
+       use crate::util::ser::{Writeable, Writer};
 
        use hex;