Receive payment onions as new InboundPayload instead of OnionHopData
authorValentine Wallace <vwallace@protonmail.com>
Fri, 24 Mar 2023 16:46:16 +0000 (12:46 -0400)
committerValentine Wallace <vwallace@protonmail.com>
Wed, 2 Aug 2023 19:54:34 +0000 (12:54 -0700)
To support route blinding, we want to split OnionHopData into two separate
structs, one for inbound onions and one for outbound onions. This is because
blinded payloads change the fields present in the onion hop data struct based
on whether we're sending vs receiving (outbound onions include encrypted blobs,
inbound onions can decrypt those blobs and contain the decrypted fields
themselves).

In upcoming commits, we'll add variants for blinded payloads to the new
InboundPayload enum.

12 files changed:
fuzz/src/bin/gen_target.sh
fuzz/src/bin/msg_onion_hop_data_target.rs [deleted file]
fuzz/src/bin/onion_hop_data_target.rs [new file with mode: 0644]
fuzz/src/lib.rs
fuzz/src/msg_targets/gen_target.sh
fuzz/src/msg_targets/mod.rs
fuzz/src/msg_targets/msg_onion_hop_data.rs [deleted file]
fuzz/src/onion_hop_data.rs [new file with mode: 0644]
fuzz/targets.h
lightning/src/ln/channelmanager.rs
lightning/src/ln/msgs.rs
lightning/src/ln/onion_utils.rs

index 34cae5107d34418b62c3a89e703bcfd896deb8be..fe17e4bab8ff356c599587446a65ec6c1033528a 100755 (executable)
@@ -20,6 +20,7 @@ GEN_TEST refund_deser
 GEN_TEST router
 GEN_TEST zbase32
 GEN_TEST indexedmap
+GEN_TEST onion_hop_data
 
 GEN_TEST msg_accept_channel msg_targets::
 GEN_TEST msg_announcement_signatures msg_targets::
@@ -51,7 +52,6 @@ GEN_TEST msg_update_add_htlc msg_targets::
 GEN_TEST msg_error_message msg_targets::
 GEN_TEST msg_channel_update msg_targets::
 
-GEN_TEST msg_onion_hop_data msg_targets::
 GEN_TEST msg_ping msg_targets::
 GEN_TEST msg_pong msg_targets::
 
diff --git a/fuzz/src/bin/msg_onion_hop_data_target.rs b/fuzz/src/bin/msg_onion_hop_data_target.rs
deleted file mode 100644 (file)
index ae21e9b..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-// This file is Copyright its original authors, visible in version control
-// history.
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-// This file is auto-generated by gen_target.sh based on target_template.txt
-// To modify it, modify target_template.txt and run gen_target.sh instead.
-
-#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
-
-#[cfg(not(fuzzing))]
-compile_error!("Fuzz targets need cfg=fuzzing");
-
-extern crate lightning_fuzz;
-use lightning_fuzz::msg_targets::msg_onion_hop_data::*;
-
-#[cfg(feature = "afl")]
-#[macro_use] extern crate afl;
-#[cfg(feature = "afl")]
-fn main() {
-       fuzz!(|data| {
-               msg_onion_hop_data_run(data.as_ptr(), data.len());
-       });
-}
-
-#[cfg(feature = "honggfuzz")]
-#[macro_use] extern crate honggfuzz;
-#[cfg(feature = "honggfuzz")]
-fn main() {
-       loop {
-               fuzz!(|data| {
-                       msg_onion_hop_data_run(data.as_ptr(), data.len());
-               });
-       }
-}
-
-#[cfg(feature = "libfuzzer_fuzz")]
-#[macro_use] extern crate libfuzzer_sys;
-#[cfg(feature = "libfuzzer_fuzz")]
-fuzz_target!(|data: &[u8]| {
-       msg_onion_hop_data_run(data.as_ptr(), data.len());
-});
-
-#[cfg(feature = "stdin_fuzz")]
-fn main() {
-       use std::io::Read;
-
-       let mut data = Vec::with_capacity(8192);
-       std::io::stdin().read_to_end(&mut data).unwrap();
-       msg_onion_hop_data_run(data.as_ptr(), data.len());
-}
-
-#[test]
-fn run_test_cases() {
-       use std::fs;
-       use std::io::Read;
-       use lightning_fuzz::utils::test_logger::StringBuffer;
-
-       use std::sync::{atomic, Arc};
-       {
-               let data: Vec<u8> = vec![0];
-               msg_onion_hop_data_run(data.as_ptr(), data.len());
-       }
-       let mut threads = Vec::new();
-       let threads_running = Arc::new(atomic::AtomicUsize::new(0));
-       if let Ok(tests) = fs::read_dir("test_cases/msg_onion_hop_data") {
-               for test in tests {
-                       let mut data: Vec<u8> = Vec::new();
-                       let path = test.unwrap().path();
-                       fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
-                       threads_running.fetch_add(1, atomic::Ordering::AcqRel);
-
-                       let thread_count_ref = Arc::clone(&threads_running);
-                       let main_thread_ref = std::thread::current();
-                       threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
-                               std::thread::spawn(move || {
-                                       let string_logger = StringBuffer::new();
-
-                                       let panic_logger = string_logger.clone();
-                                       let res = if ::std::panic::catch_unwind(move || {
-                                               msg_onion_hop_data_test(&data, panic_logger);
-                                       }).is_err() {
-                                               Some(string_logger.into_string())
-                                       } else { None };
-                                       thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
-                                       main_thread_ref.unpark();
-                                       res
-                               })
-                       ));
-                       while threads_running.load(atomic::Ordering::Acquire) > 32 {
-                               std::thread::park();
-                       }
-               }
-       }
-       let mut failed_outputs = Vec::new();
-       for (test, thread) in threads.drain(..) {
-               if let Some(output) = thread.join().unwrap() {
-                       println!("\nOutput of {}:\n{}\n", test, output);
-                       failed_outputs.push(test);
-               }
-       }
-       if !failed_outputs.is_empty() {
-               println!("Test cases which failed: ");
-               for case in failed_outputs {
-                       println!("{}", case);
-               }
-               panic!();
-       }
-}
diff --git a/fuzz/src/bin/onion_hop_data_target.rs b/fuzz/src/bin/onion_hop_data_target.rs
new file mode 100644 (file)
index 0000000..b8a3572
--- /dev/null
@@ -0,0 +1,113 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::onion_hop_data::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+       fuzz!(|data| {
+               onion_hop_data_run(data.as_ptr(), data.len());
+       });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+       loop {
+               fuzz!(|data| {
+                       onion_hop_data_run(data.as_ptr(), data.len());
+               });
+       }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+       onion_hop_data_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+       use std::io::Read;
+
+       let mut data = Vec::with_capacity(8192);
+       std::io::stdin().read_to_end(&mut data).unwrap();
+       onion_hop_data_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+       use std::fs;
+       use std::io::Read;
+       use lightning_fuzz::utils::test_logger::StringBuffer;
+
+       use std::sync::{atomic, Arc};
+       {
+               let data: Vec<u8> = vec![0];
+               onion_hop_data_run(data.as_ptr(), data.len());
+       }
+       let mut threads = Vec::new();
+       let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+       if let Ok(tests) = fs::read_dir("test_cases/onion_hop_data") {
+               for test in tests {
+                       let mut data: Vec<u8> = Vec::new();
+                       let path = test.unwrap().path();
+                       fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+                       threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+                       let thread_count_ref = Arc::clone(&threads_running);
+                       let main_thread_ref = std::thread::current();
+                       threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+                               std::thread::spawn(move || {
+                                       let string_logger = StringBuffer::new();
+
+                                       let panic_logger = string_logger.clone();
+                                       let res = if ::std::panic::catch_unwind(move || {
+                                               onion_hop_data_test(&data, panic_logger);
+                                       }).is_err() {
+                                               Some(string_logger.into_string())
+                                       } else { None };
+                                       thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+                                       main_thread_ref.unpark();
+                                       res
+                               })
+                       ));
+                       while threads_running.load(atomic::Ordering::Acquire) > 32 {
+                               std::thread::park();
+                       }
+               }
+       }
+       let mut failed_outputs = Vec::new();
+       for (test, thread) in threads.drain(..) {
+               if let Some(output) = thread.join().unwrap() {
+                       println!("\nOutput of {}:\n{}\n", test, output);
+                       failed_outputs.push(test);
+               }
+       }
+       if !failed_outputs.is_empty() {
+               println!("Test cases which failed: ");
+               for case in failed_outputs {
+                       println!("{}", case);
+               }
+               panic!();
+       }
+}
index 92142e5642313d436d86eddcdfee4e6c9fd25295..6cdeb8ab5d205f66be9083f035ea767d878cfad8 100644 (file)
@@ -28,5 +28,6 @@ pub mod process_network_graph;
 pub mod refund_deser;
 pub mod router;
 pub mod zbase32;
+pub mod onion_hop_data;
 
 pub mod msg_targets;
index 3937c5001eaba1d127a213b7002b122d9b76301a..d89df19d99a2a8f5774d2577ac2fa666f26d895d 100755 (executable)
@@ -20,7 +20,6 @@ GEN_TEST lightning::ln::msgs::ChannelReady test_msg_simple ""
 GEN_TEST lightning::ln::msgs::FundingSigned test_msg_simple ""
 GEN_TEST lightning::ln::msgs::GossipTimestampFilter test_msg_simple ""
 GEN_TEST lightning::ln::msgs::Init test_msg_simple ""
-GEN_TEST lightning::ln::msgs::OnionHopData test_msg_simple ""
 GEN_TEST lightning::ln::msgs::OpenChannel test_msg_simple ""
 GEN_TEST lightning::ln::msgs::Ping test_msg_simple ""
 GEN_TEST lightning::ln::msgs::Pong test_msg_simple ""
index fe3bd14a7c9e25e4645f0c069d5d07a9ac3e0627..302dda440cb9c3b7de0036f5298c98d9a8f231a6 100644 (file)
@@ -8,7 +8,6 @@ pub mod msg_channel_ready;
 pub mod msg_funding_signed;
 pub mod msg_gossip_timestamp_filter;
 pub mod msg_init;
-pub mod msg_onion_hop_data;
 pub mod msg_open_channel;
 pub mod msg_ping;
 pub mod msg_pong;
diff --git a/fuzz/src/msg_targets/msg_onion_hop_data.rs b/fuzz/src/msg_targets/msg_onion_hop_data.rs
deleted file mode 100644 (file)
index 59b3674..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-// This file is Copyright its original authors, visible in version control
-// history.
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-// This file is auto-generated by gen_target.sh based on msg_target_template.txt
-// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-
-use crate::msg_targets::utils::VecWriter;
-use crate::utils::test_logger;
-
-#[inline]
-pub fn msg_onion_hop_data_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
-       test_msg_simple!(lightning::ln::msgs::OnionHopData, data);
-}
-
-#[no_mangle]
-pub extern "C" fn msg_onion_hop_data_run(data: *const u8, datalen: usize) {
-       let data = unsafe { std::slice::from_raw_parts(data, datalen) };
-       test_msg_simple!(lightning::ln::msgs::OnionHopData, data);
-}
diff --git a/fuzz/src/onion_hop_data.rs b/fuzz/src/onion_hop_data.rs
new file mode 100644 (file)
index 0000000..54b283a
--- /dev/null
@@ -0,0 +1,28 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use crate::utils::test_logger;
+
+#[inline]
+pub fn onion_hop_data_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+       use lightning::util::ser::Readable;
+       let mut r = ::std::io::Cursor::new(data);
+       let _ =  <lightning::ln::msgs::InboundOnionPayload as Readable>::read(&mut r);
+}
+
+#[no_mangle]
+pub extern "C" fn onion_hop_data_run(data: *const u8, datalen: usize) {
+       use lightning::util::ser::Readable;
+       let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+       let mut r = ::std::io::Cursor::new(data);
+       let _ =  <lightning::ln::msgs::InboundOnionPayload as Readable>::read(&mut r);
+}
index eb8d66f412a5b62c9126979cf03bef0eea589d87..9b5a6d4553645215a32f6679c90d1b482d3d5c16 100644 (file)
@@ -13,6 +13,7 @@ void refund_deser_run(const unsigned char* data, size_t data_len);
 void router_run(const unsigned char* data, size_t data_len);
 void zbase32_run(const unsigned char* data, size_t data_len);
 void indexedmap_run(const unsigned char* data, size_t data_len);
+void onion_hop_data_run(const unsigned char* data, size_t data_len);
 void msg_accept_channel_run(const unsigned char* data, size_t data_len);
 void msg_announcement_signatures_run(const unsigned char* data, size_t data_len);
 void msg_channel_reestablish_run(const unsigned char* data, size_t data_len);
@@ -40,7 +41,6 @@ void msg_gossip_timestamp_filter_run(const unsigned char* data, size_t data_len)
 void msg_update_add_htlc_run(const unsigned char* data, size_t data_len);
 void msg_error_message_run(const unsigned char* data, size_t data_len);
 void msg_channel_update_run(const unsigned char* data, size_t data_len);
-void msg_onion_hop_data_run(const unsigned char* data, size_t data_len);
 void msg_ping_run(const unsigned char* data, size_t data_len);
 void msg_pong_run(const unsigned char* data, size_t data_len);
 void msg_channel_details_run(const unsigned char* data, size_t data_len);
index 2abd3c3b9e0d643b98570ffc356d6e3fbb93b88d..6bfa4f0b03a7c225a84b556d15a6cc6343b8d180 100644 (file)
@@ -2617,7 +2617,7 @@ where
        }
 
        fn construct_fwd_pending_htlc_info(
-               &self, msg: &msgs::UpdateAddHTLC, hop_data: msgs::OnionHopData, hop_hmac: [u8; 32],
+               &self, msg: &msgs::UpdateAddHTLC, hop_data: msgs::InboundOnionPayload, hop_hmac: [u8; 32],
                new_packet_bytes: [u8; onion_utils::ONION_DATA_LEN], shared_secret: [u8; 32],
                next_packet_pubkey_opt: Option<Result<PublicKey, secp256k1::Error>>
        ) -> Result<PendingHTLCInfo, InboundOnionErr> {
@@ -2626,18 +2626,18 @@ where
                        version: 0,
                        public_key: next_packet_pubkey_opt.unwrap_or(Err(secp256k1::Error::InvalidPublicKey)),
                        hop_data: new_packet_bytes,
-                       hmac: hop_hmac.clone(),
+                       hmac: hop_hmac,
                };
 
-               let short_channel_id = match hop_data.format {
-                       msgs::OnionHopDataFormat::NonFinalNode { short_channel_id } => short_channel_id,
-                       msgs::OnionHopDataFormat::FinalNode { .. } => {
+               let (short_channel_id, amt_to_forward, outgoing_cltv_value) = match hop_data {
+                       msgs::InboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } =>
+                               (short_channel_id, amt_to_forward, outgoing_cltv_value),
+                       msgs::InboundOnionPayload::Receive { .. } =>
                                return Err(InboundOnionErr {
                                        msg: "Final Node OnionHopData provided for us as an intermediary node",
                                        err_code: 0x4000 | 22,
                                        err_data: Vec::new(),
-                               })
-                       },
+                               }),
                };
 
                Ok(PendingHTLCInfo {
@@ -2645,23 +2645,25 @@ where
                                onion_packet: outgoing_packet,
                                short_channel_id,
                        },
-                       payment_hash: msg.payment_hash.clone(),
+                       payment_hash: msg.payment_hash,
                        incoming_shared_secret: shared_secret,
                        incoming_amt_msat: Some(msg.amount_msat),
-                       outgoing_amt_msat: hop_data.amt_to_forward,
-                       outgoing_cltv_value: hop_data.outgoing_cltv_value,
+                       outgoing_amt_msat: amt_to_forward,
+                       outgoing_cltv_value,
                        skimmed_fee_msat: None,
                })
        }
 
        fn construct_recv_pending_htlc_info(
-               &self, hop_data: msgs::OnionHopData, shared_secret: [u8; 32], payment_hash: PaymentHash,
+               &self, hop_data: msgs::InboundOnionPayload, shared_secret: [u8; 32], payment_hash: PaymentHash,
                amt_msat: u64, cltv_expiry: u32, phantom_shared_secret: Option<[u8; 32]>, allow_underpay: bool,
                counterparty_skimmed_fee_msat: Option<u64>,
        ) -> Result<PendingHTLCInfo, InboundOnionErr> {
-               let (payment_data, keysend_preimage, payment_metadata) = match hop_data.format {
-                       msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage, payment_metadata } =>
-                               (payment_data, keysend_preimage, payment_metadata),
+               let (payment_data, keysend_preimage, onion_amt_msat, outgoing_cltv_value, payment_metadata) = match hop_data {
+                       msgs::InboundOnionPayload::Receive {
+                               payment_data, keysend_preimage, amt_msat, outgoing_cltv_value, payment_metadata, ..
+                       } =>
+                               (payment_data, keysend_preimage, amt_msat, outgoing_cltv_value, payment_metadata),
                        _ =>
                                return Err(InboundOnionErr {
                                        err_code: 0x4000|22,
@@ -2670,7 +2672,7 @@ where
                                }),
                };
                // final_incorrect_cltv_expiry
-               if hop_data.outgoing_cltv_value > cltv_expiry {
+               if outgoing_cltv_value > cltv_expiry {
                        return Err(InboundOnionErr {
                                msg: "Upstream node set CLTV to less than the CLTV set by the sender",
                                err_code: 18,
@@ -2685,7 +2687,7 @@ where
                // payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
                // channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
                let current_height: u32 = self.best_block.read().unwrap().height();
-               if (hop_data.outgoing_cltv_value as u64) <= current_height as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
+               if (outgoing_cltv_value as u64) <= current_height as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
                        let mut err_data = Vec::with_capacity(12);
                        err_data.extend_from_slice(&amt_msat.to_be_bytes());
                        err_data.extend_from_slice(&current_height.to_be_bytes());
@@ -2694,8 +2696,8 @@ where
                                msg: "The final CLTV expiry is too soon to handle",
                        });
                }
-               if (!allow_underpay && hop_data.amt_to_forward > amt_msat) ||
-                       (allow_underpay && hop_data.amt_to_forward >
+               if (!allow_underpay && onion_amt_msat > amt_msat) ||
+                       (allow_underpay && onion_amt_msat >
                         amt_msat.saturating_add(counterparty_skimmed_fee_msat.unwrap_or(0)))
                {
                        return Err(InboundOnionErr {
@@ -2730,13 +2732,13 @@ where
                                payment_data,
                                payment_preimage,
                                payment_metadata,
-                               incoming_cltv_expiry: hop_data.outgoing_cltv_value,
+                               incoming_cltv_expiry: outgoing_cltv_value,
                        }
                } else if let Some(data) = payment_data {
                        PendingHTLCRouting::Receive {
                                payment_data: data,
                                payment_metadata,
-                               incoming_cltv_expiry: hop_data.outgoing_cltv_value,
+                               incoming_cltv_expiry: outgoing_cltv_value,
                                phantom_shared_secret,
                        }
                } else {
@@ -2751,8 +2753,8 @@ where
                        payment_hash,
                        incoming_shared_secret: shared_secret,
                        incoming_amt_msat: Some(amt_msat),
-                       outgoing_amt_msat: hop_data.amt_to_forward,
-                       outgoing_cltv_value: hop_data.outgoing_cltv_value,
+                       outgoing_amt_msat: onion_amt_msat,
+                       outgoing_cltv_value,
                        skimmed_fee_msat: counterparty_skimmed_fee_msat,
                })
        }
@@ -2816,9 +2818,8 @@ where
                };
                let (outgoing_scid, outgoing_amt_msat, outgoing_cltv_value, next_packet_pk_opt) = match next_hop {
                        onion_utils::Hop::Forward {
-                               next_hop_data: msgs::OnionHopData {
-                                       format: msgs::OnionHopDataFormat::NonFinalNode { short_channel_id }, amt_to_forward,
-                                       outgoing_cltv_value,
+                               next_hop_data: msgs::InboundOnionPayload::Forward {
+                                       short_channel_id, amt_to_forward, outgoing_cltv_value
                                }, ..
                        } => {
                                let next_pk = onion_utils::next_hop_packet_pubkey(&self.secp_ctx,
@@ -2828,9 +2829,7 @@ where
                        // We'll do receive checks in [`Self::construct_pending_htlc_info`] so we have access to the
                        // inbound channel's state.
                        onion_utils::Hop::Receive { .. } => return Ok((next_hop, shared_secret, None)),
-                       onion_utils::Hop::Forward {
-                               next_hop_data: msgs::OnionHopData { format: msgs::OnionHopDataFormat::FinalNode { .. }, .. }, ..
-                       } => {
+                       onion_utils::Hop::Forward { next_hop_data: msgs::InboundOnionPayload::Receive { .. }, .. } => {
                                return_err!("Final Node OnionHopData provided for us as an intermediary node", 0x4000 | 22, &[0; 0]);
                        }
                };
@@ -10020,16 +10019,14 @@ mod tests {
                let node = create_network(1, &node_cfg, &node_chanmgr);
                let sender_intended_amt_msat = 100;
                let extra_fee_msat = 10;
-               let hop_data = msgs::OnionHopData {
-                       amt_to_forward: 100,
+               let hop_data = msgs::InboundOnionPayload::Receive {
+                       amt_msat: 100,
                        outgoing_cltv_value: 42,
-                       format: msgs::OnionHopDataFormat::FinalNode {
-                               keysend_preimage: None,
-                               payment_metadata: None,
-                               payment_data: Some(msgs::FinalOnionHopData {
-                                       payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
-                               }),
-                       }
+                       payment_metadata: None,
+                       keysend_preimage: None,
+                       payment_data: Some(msgs::FinalOnionHopData {
+                               payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
+                       }),
                };
                // Check that if the amount we received + the penultimate hop extra fee is less than the sender
                // intended amount, we fail the payment.
@@ -10041,16 +10038,14 @@ mod tests {
                } else { panic!(); }
 
                // If amt_received + extra_fee is equal to the sender intended amount, we're fine.
-               let hop_data = msgs::OnionHopData { // This is the same hop_data as above, OnionHopData doesn't implement Clone
-                       amt_to_forward: 100,
+               let hop_data = msgs::InboundOnionPayload::Receive { // This is the same payload as above, InboundOnionPayload doesn't implement Clone
+                       amt_msat: 100,
                        outgoing_cltv_value: 42,
-                       format: msgs::OnionHopDataFormat::FinalNode {
-                               keysend_preimage: None,
-                               payment_metadata: None,
-                               payment_data: Some(msgs::FinalOnionHopData {
-                                       payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
-                               }),
-                       }
+                       payment_metadata: None,
+                       keysend_preimage: None,
+                       payment_data: Some(msgs::FinalOnionHopData {
+                               payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
+                       }),
                };
                assert!(node[0].node.construct_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
                        sender_intended_amt_msat - extra_fee_msat, 42, None, true, Some(extra_fee_msat)).is_ok());
index cfee16033be236232392caef89d09b72967affdc..590b26632fbe9216b632d33844a1e5121b5cef64 100644 (file)
@@ -1419,11 +1419,27 @@ mod fuzzy_internal_msgs {
        // These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
        // them from untrusted input):
        #[derive(Clone)]
-       pub(crate) struct FinalOnionHopData {
-               pub(crate) payment_secret: PaymentSecret,
+       pub struct FinalOnionHopData {
+               pub payment_secret: PaymentSecret,
                /// The total value, in msat, of the payment as received by the ultimate recipient.
                /// Message serialization may panic if this value is more than 21 million Bitcoin.
-               pub(crate) total_msat: u64,
+               pub total_msat: u64,
+       }
+
+       pub enum InboundOnionPayload {
+               Forward {
+                       short_channel_id: u64,
+                       /// The value, in msat, of the payment after this hop's fee is deducted.
+                       amt_to_forward: u64,
+                       outgoing_cltv_value: u32,
+               },
+               Receive {
+                       payment_data: Option<FinalOnionHopData>,
+                       payment_metadata: Option<Vec<u8>>,
+                       keysend_preimage: Option<PaymentPreimage>,
+                       amt_msat: u64,
+                       outgoing_cltv_value: u32,
+               },
        }
 
        pub(crate) enum OnionHopDataFormat {
@@ -1974,7 +1990,7 @@ impl Writeable for OnionHopData {
        }
 }
 
-impl Readable for OnionHopData {
+impl Readable for InboundOnionPayload {
        fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
                let mut amt = HighZeroBytesDroppedBigSize(0u64);
                let mut cltv_value = HighZeroBytesDroppedBigSize(0u32);
@@ -1992,39 +2008,35 @@ impl Readable for OnionHopData {
                        (5482373484, keysend_preimage, option)
                });
 
-               let format = if let Some(short_channel_id) = short_id {
-                       if payment_data.is_some() { return Err(DecodeError::InvalidValue); }
+               if amt.0 > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
+               if let Some(short_channel_id) = short_id {
+                       if payment_data.is_some() { return Err(DecodeError::InvalidValue) }
                        if payment_metadata.is_some() { return Err(DecodeError::InvalidValue); }
-                       OnionHopDataFormat::NonFinalNode {
+                       Ok(Self::Forward {
                                short_channel_id,
-                       }
+                               amt_to_forward: amt.0,
+                               outgoing_cltv_value: cltv_value.0,
+                       })
                } else {
                        if let Some(data) = &payment_data {
                                if data.total_msat > MAX_VALUE_MSAT {
                                        return Err(DecodeError::InvalidValue);
                                }
                        }
-                       OnionHopDataFormat::FinalNode {
+                       Ok(Self::Receive {
                                payment_data,
                                payment_metadata: payment_metadata.map(|w| w.0),
                                keysend_preimage,
-                       }
-               };
-
-               if amt.0 > MAX_VALUE_MSAT {
-                       return Err(DecodeError::InvalidValue);
+                               amt_msat: amt.0,
+                               outgoing_cltv_value: cltv_value.0,
+                       })
                }
-               Ok(OnionHopData {
-                       format,
-                       amt_to_forward: amt.0,
-                       outgoing_cltv_value: cltv_value.0,
-               })
        }
 }
 
 // ReadableArgs because we need onion_utils::decode_next_hop to accommodate payment packets and
 // onion message packets.
-impl ReadableArgs<()> for OnionHopData {
+impl ReadableArgs<()> for InboundOnionPayload {
        fn read<R: Read>(r: &mut R, _arg: ()) -> Result<Self, DecodeError> {
                <Self as Readable>::read(r)
        }
@@ -3525,7 +3537,7 @@ mod tests {
 
        #[test]
        fn encoding_nonfinal_onion_hop_data() {
-               let mut msg = msgs::OnionHopData {
+               let msg = msgs::OnionHopData {
                        format: OnionHopDataFormat::NonFinalNode {
                                short_channel_id: 0xdeadbeef1bad1dea,
                        },
@@ -3535,17 +3547,18 @@ mod tests {
                let encoded_value = msg.encode();
                let target_value = hex::decode("1a02080badf00d010203040404ffffffff0608deadbeef1bad1dea").unwrap();
                assert_eq!(encoded_value, target_value);
-               msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
-               if let OnionHopDataFormat::NonFinalNode { short_channel_id } = msg.format {
+
+               let inbound_msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
+               if let msgs::InboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } = inbound_msg {
                        assert_eq!(short_channel_id, 0xdeadbeef1bad1dea);
+                       assert_eq!(amt_to_forward, 0x0badf00d01020304);
+                       assert_eq!(outgoing_cltv_value, 0xffffffff);
                } else { panic!(); }
-               assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
-               assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
        }
 
        #[test]
        fn encoding_final_onion_hop_data() {
-               let mut msg = msgs::OnionHopData {
+               let msg = msgs::OnionHopData {
                        format: OnionHopDataFormat::FinalNode {
                                payment_data: None,
                                payment_metadata: None,
@@ -3557,16 +3570,18 @@ mod tests {
                let encoded_value = msg.encode();
                let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap();
                assert_eq!(encoded_value, target_value);
-               msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
-               if let OnionHopDataFormat::FinalNode { payment_data: None, .. } = msg.format { } else { panic!(); }
-               assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
-               assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
+
+               let inbound_msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
+               if let msgs::InboundOnionPayload::Receive { payment_data: None, amt_msat, outgoing_cltv_value, .. } = inbound_msg {
+                       assert_eq!(amt_msat, 0x0badf00d01020304);
+                       assert_eq!(outgoing_cltv_value, 0xffffffff);
+               } else { panic!(); }
        }
 
        #[test]
        fn encoding_final_onion_hop_data_with_secret() {
                let expected_payment_secret = PaymentSecret([0x42u8; 32]);
-               let mut msg = msgs::OnionHopData {
+               let msg = msgs::OnionHopData {
                        format: OnionHopDataFormat::FinalNode {
                                payment_data: Some(FinalOnionHopData {
                                        payment_secret: expected_payment_secret,
@@ -3581,19 +3596,21 @@ mod tests {
                let encoded_value = msg.encode();
                let target_value = hex::decode("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap();
                assert_eq!(encoded_value, target_value);
-               msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
-               if let OnionHopDataFormat::FinalNode {
+
+               let inbound_msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
+               if let msgs::InboundOnionPayload::Receive {
                        payment_data: Some(FinalOnionHopData {
                                payment_secret,
                                total_msat: 0x1badca1f
                        }),
+                       amt_msat, outgoing_cltv_value,
                        payment_metadata: None,
                        keysend_preimage: None,
-               } = msg.format {
+               } = inbound_msg  {
                        assert_eq!(payment_secret, expected_payment_secret);
+                       assert_eq!(amt_msat, 0x0badf00d01020304);
+                       assert_eq!(outgoing_cltv_value, 0xffffffff);
                } else { panic!(); }
-               assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
-               assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
        }
 
        #[test]
@@ -3743,7 +3760,7 @@ mod tests {
                // payload length to be encoded over multiple bytes rather than a single u8.
                let big_payload = encode_big_payload().unwrap();
                let mut rd = Cursor::new(&big_payload[..]);
-               <msgs::OnionHopData as Readable>::read(&mut rd).unwrap();
+               <msgs::InboundOnionPayload as Readable>::read(&mut rd).unwrap();
        }
        // see above test, needs to be a separate method for use of the serialization macros.
        fn encode_big_payload() -> Result<Vec<u8>, io::Error> {
index 52eb7bcb54298a7cc8e8fc418f77d4de66babd69..a915cf29d51b385246690a40038b3ba49929909a 100644 (file)
@@ -763,11 +763,11 @@ impl NextPacketBytes for Vec<u8> {
 pub(crate) enum Hop {
        /// This onion payload was for us, not for forwarding to a next-hop. Contains information for
        /// verifying the incoming payment.
-       Receive(msgs::OnionHopData),
+       Receive(msgs::InboundOnionPayload),
        /// This onion payload needs to be forwarded to a next-hop.
        Forward {
                /// Onion payload data used in forwarding the payment.
-               next_hop_data: msgs::OnionHopData,
+               next_hop_data: msgs::InboundOnionPayload,
                /// HMAC of the next hop's onion packet.
                next_hop_hmac: [u8; 32],
                /// Bytes of the onion packet we're forwarding.