From 4d726fcd929e8a4a465a0976863811ff79918242 Mon Sep 17 00:00:00 2001 From: shaavan Date: Mon, 20 Jan 2025 20:07:32 +0530 Subject: [PATCH 1/7] Rename Padding -> BlindedPathPadding Renaming the struct to `BlindedPathPadding` makes its purpose explicit, clarifying that it is specifically used for `Blinded{Message/Payment}Paths`. --- lightning/src/blinded_path/payment.rs | 2 +- lightning/src/blinded_path/utils.rs | 9 +++++---- lightning/src/onion_message/packet.rs | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lightning/src/blinded_path/payment.rs b/lightning/src/blinded_path/payment.rs index df7c29909f8..c1448a347b0 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -530,7 +530,7 @@ impl Readable for BlindedPaymentTlvs { (65537, payment_context, option), (65539, authentication, option), }); - let _padding: Option = _padding; + let _padding: Option = _padding; if let Some(short_channel_id) = scid { if payment_secret.is_some() { diff --git a/lightning/src/blinded_path/utils.rs b/lightning/src/blinded_path/utils.rs index 25000e84650..af78dfd3993 100644 --- a/lightning/src/blinded_path/utils.rs +++ b/lightning/src/blinded_path/utils.rs @@ -196,11 +196,12 @@ fn encrypt_payload(payload: P, encrypted_tlvs_rho: [u8; 32]) -> Ve write_adapter.encode() } -/// Blinded path encrypted payloads may be padded to ensure they are equal length. +/// A data structure used exclusively to pad blinded path payloads, ensuring they are of +/// equal length. Padding is written at Type 1 for compatibility with the lightning specification. /// -/// Reads padding to the end, ignoring what's read. -pub(crate) struct Padding {} -impl Readable for Padding { +/// For more details, see the [BOLTs Specification - Encrypted Recipient Data](https://github.com/lightning/bolts/blob/8707471dbc23245fb4d84c5f5babac1197f1583e/04-onion-routing.md#inside-encrypted_recipient_data-encrypted_data_tlv). +pub(crate) struct BlindedPathPadding {} +impl Readable for BlindedPathPadding { #[inline] fn read(reader: &mut R) -> Result { loop { diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 961ae71a2d9..69efdae33d3 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -18,7 +18,7 @@ use super::dns_resolution::DNSResolverMessage; use super::messenger::CustomOnionMessageHandler; use super::offers::OffersMessage; use crate::blinded_path::message::{BlindedMessagePath, ForwardTlvs, NextMessageHop, ReceiveTlvs}; -use crate::blinded_path::utils::Padding; +use crate::blinded_path::utils::BlindedPathPadding; use crate::crypto::streams::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; use crate::ln::msgs::DecodeError; use crate::ln::onion_utils; @@ -342,7 +342,7 @@ impl Readable for ControlTlvs { (8, next_blinding_override, option), (65537, context, option), }); - let _padding: Option = _padding; + let _padding: Option = _padding; let next_hop = match (short_channel_id, next_node_id) { (Some(_), Some(_)) => return Err(DecodeError::InvalidValue), From f32e21ecd78cd2e0a10db3d8746853a559f42c57 Mon Sep 17 00:00:00 2001 From: shaavan Date: Mon, 20 Jan 2025 20:11:48 +0530 Subject: [PATCH 2/7] Remove `Readable` for `BlindedPathPadding` Padding serves as filler data with no explicit use case, making it more efficient to ignore it rather than read and discard it. This change simplifies the code by removing unnecessary `Readable` implementation for padding. --- lightning/src/blinded_path/payment.rs | 6 ++++-- lightning/src/blinded_path/utils.rs | 17 +---------------- lightning/src/onion_message/packet.rs | 7 ++++--- 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/lightning/src/blinded_path/payment.rs b/lightning/src/blinded_path/payment.rs index c1448a347b0..7c8bb93e1e9 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -520,7 +520,10 @@ impl<'a> Writeable for BlindedPaymentTlvsRef<'a> { impl Readable for BlindedPaymentTlvs { fn read(r: &mut R) -> Result { _init_and_read_tlv_stream!(r, { - (1, _padding, option), + // Reasoning: Padding refers to filler data added to a packet to increase + // its size and obscure its actual length. Since padding contains no meaningful + // information, we can safely omit reading it here. + // (1, _padding, option), (2, scid, option), (8, next_blinding_override, option), (10, payment_relay, option), @@ -530,7 +533,6 @@ impl Readable for BlindedPaymentTlvs { (65537, payment_context, option), (65539, authentication, option), }); - let _padding: Option = _padding; if let Some(short_channel_id) = scid { if payment_secret.is_some() { diff --git a/lightning/src/blinded_path/utils.rs b/lightning/src/blinded_path/utils.rs index af78dfd3993..cea46bbb613 100644 --- a/lightning/src/blinded_path/utils.rs +++ b/lightning/src/blinded_path/utils.rs @@ -18,12 +18,9 @@ use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey}; use super::message::BlindedMessagePath; use super::{BlindedHop, BlindedPath}; use crate::crypto::streams::ChaChaPolyWriteAdapter; -use crate::ln::msgs::DecodeError; use crate::ln::onion_utils; use crate::onion_message::messenger::Destination; -use crate::util::ser::{Readable, Writeable}; - -use crate::io; +use crate::util::ser::Writeable; use core::borrow::Borrow; @@ -201,15 +198,3 @@ fn encrypt_payload(payload: P, encrypted_tlvs_rho: [u8; 32]) -> Ve /// /// For more details, see the [BOLTs Specification - Encrypted Recipient Data](https://github.com/lightning/bolts/blob/8707471dbc23245fb4d84c5f5babac1197f1583e/04-onion-routing.md#inside-encrypted_recipient_data-encrypted_data_tlv). pub(crate) struct BlindedPathPadding {} -impl Readable for BlindedPathPadding { - #[inline] - fn read(reader: &mut R) -> Result { - loop { - let mut buf = [0; 8192]; - if reader.read(&mut buf[..])? == 0 { - break; - } - } - Ok(Self {}) - } -} diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 69efdae33d3..8cc95461b73 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -18,7 +18,6 @@ use super::dns_resolution::DNSResolverMessage; use super::messenger::CustomOnionMessageHandler; use super::offers::OffersMessage; use crate::blinded_path::message::{BlindedMessagePath, ForwardTlvs, NextMessageHop, ReceiveTlvs}; -use crate::blinded_path::utils::BlindedPathPadding; use crate::crypto::streams::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter}; use crate::ln::msgs::DecodeError; use crate::ln::onion_utils; @@ -336,13 +335,15 @@ pub(crate) enum ControlTlvs { impl Readable for ControlTlvs { fn read(r: &mut R) -> Result { _init_and_read_tlv_stream!(r, { - (1, _padding, option), + // Reasoning: Padding refers to filler data added to a packet to increase + // its size and obscure its actual length. Since padding contains no meaningful + // information, we can safely omit reading it here. + // (1, _padding, option), (2, short_channel_id, option), (4, next_node_id, option), (8, next_blinding_override, option), (65537, context, option), }); - let _padding: Option = _padding; let next_hop = match (short_channel_id, next_node_id) { (Some(_), Some(_)) => return Err(DecodeError::InvalidValue), From ff7a6b0824528ef45fda1c49287eceb66ef0cb74 Mon Sep 17 00:00:00 2001 From: shaavan Date: Thu, 18 Jul 2024 18:47:43 +0530 Subject: [PATCH 3/7] Update Padding struct to contain padding length. 1. This allows setting the length of padding at the time of writing. 2. This will be used in the following commit to allow setting the padding for blinded message paths, and blinded payment paths. --- lightning/src/blinded_path/utils.rs | 34 +++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/lightning/src/blinded_path/utils.rs b/lightning/src/blinded_path/utils.rs index cea46bbb613..368e2199976 100644 --- a/lightning/src/blinded_path/utils.rs +++ b/lightning/src/blinded_path/utils.rs @@ -18,9 +18,10 @@ use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey}; use super::message::BlindedMessagePath; use super::{BlindedHop, BlindedPath}; use crate::crypto::streams::ChaChaPolyWriteAdapter; +use crate::io; use crate::ln::onion_utils; use crate::onion_message::messenger::Destination; -use crate::util::ser::Writeable; +use crate::util::ser::{Writeable, Writer}; use core::borrow::Borrow; @@ -197,4 +198,33 @@ fn encrypt_payload(payload: P, encrypted_tlvs_rho: [u8; 32]) -> Ve /// equal length. Padding is written at Type 1 for compatibility with the lightning specification. /// /// For more details, see the [BOLTs Specification - Encrypted Recipient Data](https://github.com/lightning/bolts/blob/8707471dbc23245fb4d84c5f5babac1197f1583e/04-onion-routing.md#inside-encrypted_recipient_data-encrypted_data_tlv). -pub(crate) struct BlindedPathPadding {} +pub(crate) struct BlindedPathPadding { + length: usize, +} + +impl BlindedPathPadding { + /// Creates a new [`BlindedPathPadding`] instance with a specified size. + /// Use this method when defining the padding size before writing + /// an encrypted payload. + pub fn new(length: usize) -> Self { + Self { length } + } +} + +impl Writeable for BlindedPathPadding { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { + const BUFFER_SIZE: usize = 1024; + let buffer = [0u8; BUFFER_SIZE]; + + let mut remaining = self.length; + loop { + let to_write = core::cmp::min(remaining, BUFFER_SIZE); + writer.write_all(&buffer[..to_write])?; + remaining -= to_write; + if remaining == 0 { + break; + } + } + Ok(()) + } +} From 5ce2b5e73acc8c8963660ce5d6c162c0a39d3410 Mon Sep 17 00:00:00 2001 From: shaavan Date: Mon, 30 Sep 2024 21:25:43 +0530 Subject: [PATCH 4/7] Introduce `BlindedPathWithPadding` struct Add a generic `BlindedPathWithPadding` struct to handle padding for `ForwardTlvs` and `ReceiveTlvs` used in `BlindedMessagePath` and `BlindedPaymentPath`. This struct applies padding to the contained TLVs, rounding them off to a specified value. This design provides flexibility in padding TLVs of varying sizes. The `PADDING_ROUND_OFF` value is chosen to be sufficiently large to properly mask the data type of the contained TLVs. --- lightning/src/blinded_path/utils.rs | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lightning/src/blinded_path/utils.rs b/lightning/src/blinded_path/utils.rs index 368e2199976..8b69111d589 100644 --- a/lightning/src/blinded_path/utils.rs +++ b/lightning/src/blinded_path/utils.rs @@ -228,3 +228,33 @@ impl Writeable for BlindedPathPadding { Ok(()) } } + +/// Padding storage requires two extra bytes: +/// - One byte for the type. +/// - One byte for the padding length. +/// This constant accounts for that overhead. +const TLV_OVERHEAD: usize = 2; + +/// A generic struct that applies padding to blinded path TLVs, rounding their size off to `round_off` +pub(crate) struct BlindedPathWithPadding { + pub(crate) tlvs: T, + pub(crate) round_off: usize, +} + +impl Writeable for BlindedPathWithPadding { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { + let tlv_length = self.tlvs.serialized_length(); + let total_length = tlv_length + TLV_OVERHEAD; + + let padding_length = + (total_length + self.round_off - 1) / self.round_off * self.round_off - total_length; + + let padding = Some(BlindedPathPadding::new(padding_length)); + + encode_tlv_stream!(writer, { + (1, padding, option), + }); + + self.tlvs.write(writer) + } +} From 8ad34ef4082dff92fb8034c4586244aa48e49d5a Mon Sep 17 00:00:00 2001 From: shaavan Date: Sat, 5 Oct 2024 17:19:07 +0530 Subject: [PATCH 5/7] Introduce Padding for BlindedMessage Paths. A note of Compact Blinded Paths: Compact Blinded paths are intended to be as short as possible. So to maintain there compactness, we don't apply padding to them. --- lightning/src/blinded_path/message.rs | 24 ++++++++++++++++++------ lightning/src/blinded_path/payment.rs | 1 - 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs index 441a2c2a625..5a5821043c8 100644 --- a/lightning/src/blinded_path/message.rs +++ b/lightning/src/blinded_path/message.rs @@ -14,7 +14,7 @@ use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey}; #[allow(unused_imports)] use crate::prelude::*; -use crate::blinded_path::utils; +use crate::blinded_path::utils::{self, BlindedPathWithPadding}; use crate::blinded_path::{BlindedHop, BlindedPath, Direction, IntroductionNode, NodeIdLookUp}; use crate::crypto::streams::ChaChaPolyReadAdapter; use crate::io; @@ -265,7 +265,6 @@ impl Writeable for ForwardTlvs { NextMessageHop::NodeId(pubkey) => (Some(pubkey), None), NextMessageHop::ShortChannelId(scid) => (None, Some(scid)), }; - // TODO: write padding encode_tlv_stream!(writer, { (2, short_channel_id, option), (4, next_node_id, option), @@ -277,7 +276,6 @@ impl Writeable for ForwardTlvs { impl Writeable for ReceiveTlvs { fn write(&self, writer: &mut W) -> Result<(), io::Error> { - // TODO: write padding encode_tlv_stream!(writer, { (65537, self.context, option), }); @@ -495,6 +493,10 @@ impl_writeable_tlv_based!(DNSResolverContext, { (0, nonce, required), }); +/// Represents the padding round off size (in bytes) that is used +/// to pad message blinded path's [`BlindedHop`] +pub(crate) const MESSAGE_PADDING_ROUND_OFF: usize = 100; + /// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`. pub(super) fn blinded_hops( secp_ctx: &Secp256k1, intermediate_nodes: &[MessageForwardNode], @@ -504,6 +506,8 @@ pub(super) fn blinded_hops( .iter() .map(|node| node.node_id) .chain(core::iter::once(recipient_node_id)); + let is_compact = intermediate_nodes.iter().any(|node| node.short_channel_id.is_some()); + let tlvs = pks .clone() .skip(1) // The first node's TLVs contains the next node's pubkey @@ -517,7 +521,15 @@ pub(super) fn blinded_hops( }) .chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { context: Some(context) }))); - let path = pks.zip(tlvs); - - utils::construct_blinded_hops(secp_ctx, path, session_priv) + if is_compact { + let path = pks.zip(tlvs); + utils::construct_blinded_hops(secp_ctx, path, session_priv) + } else { + let path = + pks.zip(tlvs.map(|tlv| BlindedPathWithPadding { + tlvs: tlv, + round_off: MESSAGE_PADDING_ROUND_OFF, + })); + utils::construct_blinded_hops(secp_ctx, path, session_priv) + } } diff --git a/lightning/src/blinded_path/payment.rs b/lightning/src/blinded_path/payment.rs index 7c8bb93e1e9..e999645879f 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -508,7 +508,6 @@ impl Writeable for UnauthenticatedReceiveTlvs { impl<'a> Writeable for BlindedPaymentTlvsRef<'a> { fn write(&self, w: &mut W) -> Result<(), io::Error> { - // TODO: write padding match self { Self::Forward(tlvs) => tlvs.write(w)?, Self::Receive(tlvs) => tlvs.write(w)?, From 5d6881f44e6cdda66253c1f8c3dc1683b772a3e2 Mon Sep 17 00:00:00 2001 From: shaavan Date: Sat, 5 Oct 2024 17:24:23 +0530 Subject: [PATCH 6/7] Introduce Padding for BlindedPaymentPaths --- lightning/src/blinded_path/payment.rs | 10 ++++++++-- lightning/src/ln/blinded_payment_tests.rs | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lightning/src/blinded_path/payment.rs b/lightning/src/blinded_path/payment.rs index e999645879f..7c205ff8c48 100644 --- a/lightning/src/blinded_path/payment.rs +++ b/lightning/src/blinded_path/payment.rs @@ -14,7 +14,7 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey}; -use crate::blinded_path::utils; +use crate::blinded_path::utils::{self, BlindedPathWithPadding}; use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NodeIdLookUp}; use crate::crypto::streams::ChaChaPolyReadAdapter; use crate::io; @@ -560,6 +560,10 @@ impl Readable for BlindedPaymentTlvs { } } +/// Represents the padding round off size (in bytes) that +/// is used to pad payment bilnded path's [`BlindedHop`] +pub(crate) const PAYMENT_PADDING_ROUND_OFF: usize = 30; + /// Construct blinded payment hops for the given `intermediate_nodes` and payee info. pub(super) fn blinded_hops( secp_ctx: &Secp256k1, intermediate_nodes: &[PaymentForwardNode], payee_node_id: PublicKey, @@ -572,7 +576,9 @@ pub(super) fn blinded_hops( .map(|node| BlindedPaymentTlvsRef::Forward(&node.tlvs)) .chain(core::iter::once(BlindedPaymentTlvsRef::Receive(&payee_tlvs))); - let path = pks.zip(tlvs); + let path = pks.zip( + tlvs.map(|tlv| BlindedPathWithPadding { tlvs: tlv, round_off: PAYMENT_PADDING_ROUND_OFF }), + ); utils::construct_blinded_hops(secp_ctx, path, session_priv) } diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 9fc722f8205..07318bae60d 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -353,7 +353,7 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) { let mut route_params = get_blinded_route_parameters(amt_msat, payment_secret, 1, 1_0000_0000, nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2, &chan_upd_2_3], &chanmon_cfgs[3].keys_manager); - route_params.payment_params.max_path_length = 17; + route_params.payment_params.max_path_length = 16; let route = get_route(&nodes[0], &route_params).unwrap(); node_cfgs[0].router.expect_find_route(route_params.clone(), Ok(route.clone())); @@ -878,6 +878,8 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) { nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_1_2], &chanmon_cfgs[2].keys_manager); + route_params.payment_params.max_path_length = 17; + let route = if check == ReceiveCheckFail::ProcessPendingHTLCsCheck { let mut route = get_route(&nodes[0], &route_params).unwrap(); // Set the final CLTV expiry too low to trigger the failure in process_pending_htlc_forwards. From f8ffc1abc347f7c20ea005862ebf8c0b97ca79ea Mon Sep 17 00:00:00 2001 From: shaavan Date: Tue, 1 Oct 2024 19:27:05 +0530 Subject: [PATCH 7/7] Introduce test for Padding Add test to verify blinded message and payment path padding. --- lightning/src/blinded_path/utils.rs | 15 +++++ lightning/src/ln/blinded_payment_tests.rs | 39 +++++++++++- .../src/onion_message/functional_tests.rs | 62 ++++++++++++++++++- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/lightning/src/blinded_path/utils.rs b/lightning/src/blinded_path/utils.rs index 8b69111d589..b17fa01bbcf 100644 --- a/lightning/src/blinded_path/utils.rs +++ b/lightning/src/blinded_path/utils.rs @@ -258,3 +258,18 @@ impl Writeable for BlindedPathWithPadding { self.tlvs.write(writer) } } + +#[cfg(test)] +/// Checks if all the packets in the blinded path are properly padded. +pub fn is_padded(hops: &[BlindedHop], padding_round_off: usize) -> bool { + let first_hop = hops.first().expect("BlindedPath must have at least one hop"); + let first_payload_size = first_hop.encrypted_payload.len(); + + // The unencrypted payload data is padded before getting encrypted. + // Assuming the first payload is padded properly, get the extra data length. + let extra_length = first_payload_size % padding_round_off; + hops.iter().all(|hop| { + // Check that every packet is padded to the round off length subtracting the extra length. + (hop.encrypted_payload.len() - extra_length) % padding_round_off == 0 + }) +} diff --git a/lightning/src/ln/blinded_payment_tests.rs b/lightning/src/ln/blinded_payment_tests.rs index 07318bae60d..a33e8cbe720 100644 --- a/lightning/src/ln/blinded_payment_tests.rs +++ b/lightning/src/ln/blinded_payment_tests.rs @@ -13,7 +13,8 @@ use bitcoin::secp256k1::{PublicKey, Scalar, Secp256k1, SecretKey, schnorr}; use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature}; use crate::blinded_path; -use crate::blinded_path::payment::{BlindedPaymentPath, Bolt12RefundContext, PaymentForwardNode, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentRelay, UnauthenticatedReceiveTlvs}; +use crate::blinded_path::payment::{BlindedPaymentPath, Bolt12RefundContext, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentForwardNode, PaymentRelay, UnauthenticatedReceiveTlvs, PAYMENT_PADDING_ROUND_OFF}; +use crate::blinded_path::utils::is_padded; use crate::events::{Event, HTLCDestination, PaymentFailureReason}; use crate::ln::types::ChannelId; use crate::types::payment::{PaymentHash, PaymentSecret}; @@ -1428,6 +1429,42 @@ fn fails_receive_tlvs_authentication() { ); } +#[test] +fn blinded_payment_path_padding() { + // Make sure that for a blinded payment path, all encrypted payloads are padded to equal lengths. + let chanmon_cfgs = create_chanmon_cfgs(5); + let node_cfgs = create_node_cfgs(5, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, None]); + let mut nodes = create_network(5, &node_cfgs, &node_chanmgrs); + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); + create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0); + let chan_upd_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0).0.contents; + let chan_upd_3_4 = create_announced_chan_between_nodes_with_value(&nodes, 3, 4, 1_000_000, 0).0.contents; + + // Get all our nodes onto the same height so payments don't fail for CLTV violations. + connect_blocks(&nodes[0], nodes[4].best_block_info().1 - nodes[0].best_block_info().1); + connect_blocks(&nodes[1], nodes[4].best_block_info().1 - nodes[1].best_block_info().1); + connect_blocks(&nodes[2], nodes[4].best_block_info().1 - nodes[2].best_block_info().1); + assert_eq!(nodes[4].best_block_info().1, nodes[3].best_block_info().1); + + let amt_msat = 5000; + let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[4], Some(amt_msat), None); + + let blinded_path = blinded_payment_path(payment_secret, 1, 1_0000_0000, + nodes.iter().skip(2).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_2_3, &chan_upd_3_4], + &chanmon_cfgs[4].keys_manager + ); + + assert!(is_padded(&blinded_path.blinded_hops(), PAYMENT_PADDING_ROUND_OFF)); + + let route_params = RouteParameters::from_payment_params_and_value(PaymentParameters::blinded(vec![blinded_path]), amt_msat); + + nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap(); + check_added_monitors(&nodes[0], 1); + pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3], &nodes[4]]], amt_msat, payment_hash, payment_secret); + claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3], &nodes[4]], payment_preimage); +} + fn secret_from_hex(hex: &str) -> SecretKey { SecretKey::from_slice(&>::from_hex(hex).unwrap()).unwrap() } diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs index e007bb616d2..b28819ee692 100644 --- a/lightning/src/onion_message/functional_tests.rs +++ b/lightning/src/onion_message/functional_tests.rs @@ -21,8 +21,9 @@ use super::offers::{OffersMessage, OffersMessageHandler}; use super::packet::{OnionMessageContents, Packet}; use crate::blinded_path::message::{ AsyncPaymentsContext, BlindedMessagePath, DNSResolverContext, MessageContext, - MessageForwardNode, OffersContext, + MessageForwardNode, OffersContext, MESSAGE_PADDING_ROUND_OFF, }; +use crate::blinded_path::utils::is_padded; use crate::blinded_path::EmptyNodeIdLookUp; use crate::events::{Event, EventsProvider}; use crate::ln::msgs::{self, BaseMessageHandler, DecodeError, OnionMessageHandler}; @@ -596,6 +597,65 @@ fn too_big_packet_error() { assert_eq!(err, SendError::TooBigPacket); } +#[test] +fn test_blinded_path_padding_for_full_length_path() { + // Check that for a full blinded path, all encrypted payload are padded to rounded-off length. + let nodes = create_nodes(4); + let test_msg = TestCustomMessage::Pong; + + let secp_ctx = Secp256k1::new(); + let intermediate_nodes = [ + MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }, + MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None }, + ]; + // Update the context to create a larger final receive TLVs, ensuring that + // the hop sizes vary before padding. + let context = MessageContext::Custom(vec![0u8; 42]); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[3].node_id, + context, + &*nodes[3].entropy_source, + &secp_ctx, + ) + .unwrap(); + + assert!(is_padded(&blinded_path.blinded_hops(), MESSAGE_PADDING_ROUND_OFF)); + + let destination = Destination::BlindedPath(blinded_path); + let instructions = MessageSendInstructions::WithoutReplyPath { destination }; + + nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap(); + nodes[3].custom_message_handler.expect_message(TestCustomMessage::Pong); + pass_along_path(&nodes); +} + +#[test] +fn test_blinded_path_no_padding_for_compact_path() { + // Check that for a compact blinded path, no padding is applied. + let nodes = create_nodes(4); + let secp_ctx = Secp256k1::new(); + + // Include some short_channel_id, so that MessageRouter uses this to create compact blinded paths. + let intermediate_nodes = [ + MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: Some(24) }, + MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: Some(25) }, + ]; + // Update the context to create a larger final receive TLVs, ensuring that + // the hop sizes vary before padding. + let context = MessageContext::Custom(vec![0u8; 42]); + let blinded_path = BlindedMessagePath::new( + &intermediate_nodes, + nodes[3].node_id, + context, + &*nodes[3].entropy_source, + &secp_ctx, + ) + .unwrap(); + + assert!(!is_padded(&blinded_path.blinded_hops(), MESSAGE_PADDING_ROUND_OFF)); +} + #[test] fn we_are_intro_node() { // If we are sending straight to a blinded path and we are the introduction node, we need to