Always pad `BlindedMessagePath` hop data to a consistent length
What changed, and why it matters
This commit fixes a privacy leak in Lightning onion messages. When building a blinded path with fake 'dummy' hops, the program previously did not always hide the size of the data carried at each hop. That made the dummy hops easy to spot, defeating their purpose. The patch adds a new padding mode so that every non-final hop is padded to the same length, keeping dummy hops indistinguishable from real ones. It also lets compact paths use this same minimal padding instead of skipping padding entirely.
Review callers of BlindedMessagePath constructors to ensure the new `compact_padding` argument is set appropriately; prefer `false` for maximum privacy unless path size is constrained (e.g., BOLT 12 QR codes). Verify that compact padding still yields identical intermediate payload lengths in all configurations.
Security signals we found
Privacy leak: dummy hops identifiable by payload length
Padding policy change in blinded path construction
New API parameter `compact_padding` added to BlindedMessagePath constructors
Functional tests updated to assert uniform intermediate hop lengths under compact padding
DefaultMessageRouter compact paths now use compact padding regardless of short_channel_id availability
Evidence from the diff
BlindedMessagePath construction now takes a compact_padding flag. When true, all intermediate (non-final) hop TLVs are padded to the same serialized length, preventing dummy hops from being identified by their payload size. When false, the existing MESSAGE_PADDING_ROUND_OFF padding is preserved. The utils.rs change allows round_off == 0 to mean no padding. The messenger now passes size_constrained (true for BOLT 12 QR-code contexts) as the padding flag, so compact paths still get uniform-length intermediate payloads. This is a privacy hardening fix, not a cryptographic break.
Changed components
lightning/src/blinded_path/message.rslightning/src/blinded_path/utils.rslightning/src/onion_message/messenger.rslightning/src/offers/flow.rslightning-dns-resolver/src/lib.rsInspect captured patch +208 / −146
diff --git a/lightning-dns-resolver/src/lib.rs b/lightning-dns-resolver/src/lib.rs
index f5b1d53..765557d 100644
--- a/lightning-dns-resolver/src/lib.rs
+++ b/lightning-dns-resolver/src/lib.rs
@@ -236,6 +236,7 @@ mod test {
recipient,
local_node_receive_key,
context,
+ false,
&keys,
secp_ctx,
)])
@@ -345,6 +346,7 @@ mod test {
payer_id,
receive_key,
query_context,
+ false,
&*payer_keys,
&secp_ctx,
);
diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs
index 8210d2d..84a42ff 100644
--- a/lightning/src/blinded_path/message.rs
+++ b/lightning/src/blinded_path/message.rs
@@ -54,21 +54,38 @@ impl Readable for BlindedMessagePath {
impl BlindedMessagePath {
/// Create a one-hop blinded path for a message.
+ ///
+ /// `compact_padding` selects between space-inefficient padding which better hides contents and
+ /// a space-constrained padding which does very little to hide the contents, especially for the
+ /// last hop. It should only be set when the blinded path needs to be as compact as possible.
pub fn one_hop<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
recipient_node_id: PublicKey, local_node_receive_key: ReceiveAuthKey,
- context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1<T>,
+ context: MessageContext, compact_padding: bool, entropy_source: ES,
+ secp_ctx: &Secp256k1<T>,
) -> Self
where
ES::Target: EntropySource,
{
- Self::new(&[], recipient_node_id, local_node_receive_key, context, entropy_source, secp_ctx)
+ Self::new(
+ &[],
+ recipient_node_id,
+ local_node_receive_key,
+ context,
+ compact_padding,
+ entropy_source,
+ secp_ctx,
+ )
}
/// Create a path for an onion message, to be forwarded along `node_pks`.
+ ///
+ /// `compact_padding` selects between space-inefficient padding which better hides contents and
+ /// a space-constrained padding which does very little to hide the contents, especially for the
+ /// last hop. It should only be set when the blinded path needs to be as compact as possible.
pub fn new<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey,
- local_node_receive_key: ReceiveAuthKey, context: MessageContext, entropy_source: ES,
- secp_ctx: &Secp256k1<T>,
+ local_node_receive_key: ReceiveAuthKey, context: MessageContext, compact_padding: bool,
+ entropy_source: ES, secp_ctx: &Secp256k1<T>,
) -> Self
where
ES::Target: EntropySource,
@@ -79,6 +96,7 @@ impl BlindedMessagePath {
0,
local_node_receive_key,
context,
+ compact_padding,
entropy_source,
secp_ctx,
)
@@ -86,12 +104,15 @@ impl BlindedMessagePath {
/// Same as [`BlindedMessagePath::new`], but allows specifying a number of dummy hops.
///
- /// Note:
- /// At most [`MAX_DUMMY_HOPS_COUNT`] dummy hops can be added to the blinded path.
+ /// `compact_padding` selects between space-inefficient padding which better hides contents and
+ /// a space-constrained padding which does very little to hide the contents, especially for the
+ /// last hop. It should only be set when the blinded path needs to be as compact as possible.
+ ///
+ /// Note: At most [`MAX_DUMMY_HOPS_COUNT`] dummy hops can be added to the blinded path.
pub fn new_with_dummy_hops<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey,
dummy_hop_count: usize, local_node_receive_key: ReceiveAuthKey, context: MessageContext,
- entropy_source: ES, secp_ctx: &Secp256k1<T>,
+ compact_padding: bool, entropy_source: ES, secp_ctx: &Secp256k1<T>,
) -> Self
where
ES::Target: EntropySource,
@@ -114,6 +135,7 @@ impl BlindedMessagePath {
context,
&blinding_secret,
local_node_receive_key,
+ compact_padding,
),
})
}
@@ -714,7 +736,7 @@ pub const MAX_DUMMY_HOPS_COUNT: usize = 10;
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[MessageForwardNode],
recipient_node_id: PublicKey, dummy_hop_count: usize, context: MessageContext,
- session_priv: &SecretKey, local_node_receive_key: ReceiveAuthKey,
+ session_priv: &SecretKey, local_node_receive_key: ReceiveAuthKey, compact_padding: bool,
) -> Vec<BlindedHop> {
let dummy_count = cmp::min(dummy_hop_count, MAX_DUMMY_HOPS_COUNT);
let pks = intermediate_nodes
@@ -724,9 +746,8 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
core::iter::repeat((recipient_node_id, Some(local_node_receive_key))).take(dummy_count),
)
.chain(core::iter::once((recipient_node_id, Some(local_node_receive_key))));
- let is_compact = intermediate_nodes.iter().any(|node| node.short_channel_id.is_some());
- let tlvs = pks
+ let intermediate_tlvs = pks
.clone()
.skip(1) // The first node's TLVs contains the next node's pubkey
.zip(intermediate_nodes.iter().map(|node| node.short_channel_id))
@@ -737,18 +758,43 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
.map(|next_hop| {
ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None })
})
- .chain((0..dummy_count).map(|_| ControlTlvs::Dummy))
- .chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { context: Some(context) })));
-
- if is_compact {
- let path = pks.zip(tlvs);
- utils::construct_blinded_hops(secp_ctx, path, session_priv)
+ .chain((0..dummy_count).map(|_| ControlTlvs::Dummy));
+
+ let max_intermediate_len =
+ intermediate_tlvs.clone().map(|tlvs| tlvs.serialized_length()).max().unwrap_or(0);
+ let have_intermediate_one_byte_smaller =
+ intermediate_tlvs.clone().any(|tlvs| tlvs.serialized_length() == max_intermediate_len - 1);
+
+ let round_off = if compact_padding {
+ // We can only pad by a minimum of two bytes (we can only go from no-TLV to a type + length
+ // byte). Thus, if there are any intermediate hops that need to be padded by exactly one
+ // byte, we have to instead pad everything by two.
+ if have_intermediate_one_byte_smaller {
+ max_intermediate_len + 2
+ } else {
+ max_intermediate_len
+ }
} 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)
- }
+ MESSAGE_PADDING_ROUND_OFF
+ };
+
+ let tlvs = intermediate_tlvs
+ .map(|tlvs| {
+ let res = BlindedPathWithPadding { tlvs, round_off };
+ if compact_padding {
+ debug_assert_eq!(res.serialized_length(), max_intermediate_len);
+ } else {
+ // We don't currently ever push extra fields to intermediate hops, so they should
+ // never go over `MESSAGE_PADDING_ROUND_OFF`.
+ debug_assert_eq!(res.serialized_length(), MESSAGE_PADDING_ROUND_OFF);
+ }
+ res
+ })
+ .chain(core::iter::once(BlindedPathWithPadding {
+ tlvs: ControlTlvs::Receive(ReceiveTlvs { context: Some(context) }),
+ round_off: if compact_padding { 0 } else { MESSAGE_PADDING_ROUND_OFF },
+ }));
+
+ let path = pks.zip(tlvs);
+ utils::construct_blinded_hops(secp_ctx, path, session_priv)
}
diff --git a/lightning/src/blinded_path/utils.rs b/lightning/src/blinded_path/utils.rs
index 8894f37..339b433 100644
--- a/lightning/src/blinded_path/utils.rs
+++ b/lightning/src/blinded_path/utils.rs
@@ -256,9 +256,12 @@ impl<T: Writeable> Writeable for BlindedPathWithPadding<T> {
let tlv_length = self.tlvs.serialized_length();
let total_length = tlv_length + TLV_OVERHEAD;
- let padding_length = total_length.div_ceil(self.round_off) * self.round_off - total_length;
-
- let padding = Some(BlindedPathPadding::new(padding_length));
+ let padding = if self.round_off == 0 || tlv_length % self.round_off == 0 {
+ None
+ } else {
+ let length = total_length.div_ceil(self.round_off) * self.round_off - total_length;
+ Some(BlindedPathPadding::new(length))
+ };
encode_tlv_stream!(writer, {
(1, padding, option),
diff --git a/lightning/src/offers/flow.rs b/lightning/src/offers/flow.rs
index f9bd109..00ef0ac 100644
--- a/lightning/src/offers/flow.rs
+++ b/lightning/src/offers/flow.rs
@@ -1319,6 +1319,7 @@ where
num_dummy_hops,
self.receive_auth_key,
context,
+ false,
&*entropy,
&self.secp_ctx,
)
diff --git a/lightning/src/onion_message/functional_tests.rs b/lightning/src/onion_message/functional_tests.rs
index 605a81a..75e2aaf 100644
--- a/lightning/src/onion_message/functional_tests.rs
+++ b/lightning/src/onion_message/functional_tests.rs
@@ -436,8 +436,9 @@ fn one_blinded_hop() {
let context = MessageContext::Custom(Vec::new());
let entropy = &*nodes[1].entropy_source;
let receive_key = nodes[1].messenger.node_signer.get_receive_auth_key();
+ let node_id = nodes[1].node_id;
let blinded_path =
- BlindedMessagePath::new(&[], nodes[1].node_id, receive_key, context, entropy, &secp_ctx);
+ BlindedMessagePath::new(&[], node_id, receive_key, context, false, entropy, &secp_ctx);
let destination = Destination::BlindedPath(blinded_path);
let instructions = MessageSendInstructions::WithoutReplyPath { destination };
nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap();
@@ -450,18 +451,15 @@ fn blinded_path_with_dummy_hops() {
let nodes = create_nodes(2);
let test_msg = TestCustomMessage::Pong;
- let secp_ctx = Secp256k1::new();
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[1].entropy_source;
- let receive_key = nodes[1].messenger.node_signer.get_receive_auth_key();
let blinded_path = BlindedMessagePath::new_with_dummy_hops(
&[],
nodes[1].node_id,
TEST_DUMMY_HOP_COUNT,
- receive_key,
- context,
- entropy,
- &secp_ctx,
+ nodes[1].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[1].entropy_source,
+ &Secp256k1::new(),
);
// Ensure that dummy hops are added to the blinded path.
assert_eq!(blinded_path.blinded_hops().len(), 6);
@@ -477,19 +475,16 @@ fn two_unblinded_two_blinded() {
let nodes = create_nodes(5);
let test_msg = TestCustomMessage::Pong;
- let secp_ctx = Secp256k1::new();
let intermediate_nodes =
[MessageForwardNode { node_id: nodes[3].node_id, short_channel_id: None }];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[4].entropy_source;
- let receive_key = nodes[4].messenger.node_signer.get_receive_auth_key();
let blinded_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[4].node_id,
- receive_key,
- context,
- entropy,
- &secp_ctx,
+ nodes[4].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[4].entropy_source,
+ &Secp256k1::new(),
);
let path = OnionMessagePath {
intermediate_nodes: vec![nodes[1].node_id, nodes[2].node_id],
@@ -507,21 +502,18 @@ fn three_blinded_hops() {
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 },
];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[3].entropy_source;
- let receive_key = nodes[3].messenger.node_signer.get_receive_auth_key();
let blinded_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[3].node_id,
- receive_key,
- context,
- entropy,
- &secp_ctx,
+ nodes[3].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[3].entropy_source,
+ &Secp256k1::new(),
);
let destination = Destination::BlindedPath(blinded_path);
let instructions = MessageSendInstructions::WithoutReplyPath { destination };
@@ -548,8 +540,9 @@ fn async_response_over_one_blinded_hop() {
let context = MessageContext::Custom(Vec::new());
let entropy = &*nodes[1].entropy_source;
let receive_key = nodes[1].messenger.node_signer.get_receive_auth_key();
+ let node_id = nodes[1].node_id;
let reply_path =
- BlindedMessagePath::new(&[], nodes[1].node_id, receive_key, context, entropy, &secp_ctx);
+ BlindedMessagePath::new(&[], node_id, receive_key, context, false, entropy, &secp_ctx);
// 4. Create a responder using the reply path for Alice.
let responder = Some(Responder::new(reply_path));
@@ -590,7 +583,7 @@ fn async_response_with_reply_path_succeeds() {
let entropy = &*bob.entropy_source;
let receive_key = bob.messenger.node_signer.get_receive_auth_key();
let reply_path =
- BlindedMessagePath::new(&[], bob.node_id, receive_key, context, entropy, &secp_ctx);
+ BlindedMessagePath::new(&[], bob.node_id, receive_key, context, false, entropy, &secp_ctx);
// Alice asynchronously responds to Bob, expecting a response back from him.
let responder = Responder::new(reply_path);
@@ -632,7 +625,7 @@ fn async_response_with_reply_path_fails() {
let entropy = &*bob.entropy_source;
let receive_key = bob.messenger.node_signer.get_receive_auth_key();
let reply_path =
- BlindedMessagePath::new(&[], bob.node_id, receive_key, context, entropy, &secp_ctx);
+ BlindedMessagePath::new(&[], bob.node_id, receive_key, context, false, entropy, &secp_ctx);
// Alice tries to asynchronously respond to Bob, but fails because the nodes are unannounced and
// disconnected. Thus, a reply path could no be created for the response.
@@ -668,28 +661,26 @@ fn too_big_packet_error() {
#[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.
+ // Check that for a full blinded path without compact padding, 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 entropy = &*nodes[3].entropy_source;
- let receive_key = nodes[3].messenger.node_signer.get_receive_auth_key();
+ // Build with a larger context to create a larger final receive TLVs, ensuring that the hop
+ // sizes vary before padding.
let blinded_path = BlindedMessagePath::new_with_dummy_hops(
&intermediate_nodes,
nodes[3].node_id,
TEST_DUMMY_HOP_COUNT,
- receive_key,
- context,
- entropy,
- &secp_ctx,
+ nodes[3].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(vec![0u8; 42]),
+ false,
+ &*nodes[3].entropy_source,
+ &Secp256k1::new(),
);
assert!(is_padded(&blinded_path.blinded_hops(), MESSAGE_PADDING_ROUND_OFF));
@@ -703,32 +694,72 @@ fn test_blinded_path_padding_for_full_length_path() {
}
#[test]
-fn test_blinded_path_no_padding_for_compact_path() {
- // Check that for a compact blinded path, no padding is applied.
+fn test_blinded_path_compact_padding() {
+ // Check that for a blinded path with non-SCID intermediate hops with compact padding, no extra
+ // 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: None },
+ MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
+ ];
+ // Build with a larger context to create a larger final receive TLVs, ensuring that the hop
+ // sizes vary before padding.
+ let blinded_path = BlindedMessagePath::new_with_dummy_hops(
+ &intermediate_nodes,
+ nodes[3].node_id,
+ TEST_DUMMY_HOP_COUNT,
+ nodes[3].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(vec![0u8; 42]),
+ true,
+ &*nodes[3].entropy_source,
+ &Secp256k1::new(),
+ );
+
+ let hops = blinded_path.blinded_hops();
+ assert!(!is_padded(&hops, MESSAGE_PADDING_ROUND_OFF));
+ assert_eq!(hops.len(), TEST_DUMMY_HOP_COUNT + 3);
+ for hop in hops.iter().take(TEST_DUMMY_HOP_COUNT + 2) {
+ assert_eq!(hops[0].encrypted_payload.len(), hop.encrypted_payload.len());
+ }
+ // Check the actual encrypted payload lengths, which may change in the future but serves to
+ // ensure that this and test_compact_blinded_path_compact_padding, below, differ.
+ assert_eq!(hops[0].encrypted_payload.len(), 51);
+}
+
+#[test]
+fn test_compact_blinded_path_compact_padding() {
+ // Check that for a blinded path with SCID intermediate hops with compact padding, no extra
+ // padding is applied.
+ let nodes = create_nodes(4);
+
+ // 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 entropy = &*nodes[3].entropy_source;
- let receive_key = nodes[3].messenger.node_signer.get_receive_auth_key();
+ // Build with a larger context to create a larger final receive TLVs, ensuring that the hop
+ // sizes vary before padding.
let blinded_path = BlindedMessagePath::new_with_dummy_hops(
&intermediate_nodes,
nodes[3].node_id,
TEST_DUMMY_HOP_COUNT,
- receive_key,
- context,
- entropy,
- &secp_ctx,
+ nodes[3].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(vec![0u8; 42]),
+ true,
+ &*nodes[3].entropy_source,
+ &Secp256k1::new(),
);
- assert!(!is_padded(&blinded_path.blinded_hops(), MESSAGE_PADDING_ROUND_OFF));
+ let hops = blinded_path.blinded_hops();
+ assert!(!is_padded(&hops, MESSAGE_PADDING_ROUND_OFF));
+ assert_eq!(hops.len(), TEST_DUMMY_HOP_COUNT + 3);
+ for hop in hops.iter().take(TEST_DUMMY_HOP_COUNT + 2) {
+ assert_eq!(hops[0].encrypted_payload.len(), hop.encrypted_payload.len());
+ }
+ // Check the actual encrypted payload lengths, which may change in the future but serves to
+ // ensure that this and test_blinded_path_compact_padding, above, differ.
+ assert_eq!(hops[0].encrypted_payload.len(), 26);
}
#[test]
@@ -743,15 +774,13 @@ fn we_are_intro_node() {
MessageForwardNode { node_id: nodes[0].node_id, short_channel_id: None },
MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[2].entropy_source;
- let receive_key = nodes[2].messenger.node_signer.get_receive_auth_key();
let blinded_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[2].node_id,
- receive_key,
- context,
- entropy,
+ nodes[2].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[2].entropy_source,
&secp_ctx,
);
let destination = Destination::BlindedPath(blinded_path);
@@ -764,15 +793,13 @@ fn we_are_intro_node() {
// Try with a two-hop blinded path where we are the introduction node.
let intermediate_nodes =
[MessageForwardNode { node_id: nodes[0].node_id, short_channel_id: None }];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[1].entropy_source;
- let receive_key = nodes[1].messenger.node_signer.get_receive_auth_key();
let blinded_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[1].node_id,
- receive_key,
- context,
- entropy,
+ nodes[1].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[1].entropy_source,
&secp_ctx,
);
let destination = Destination::BlindedPath(blinded_path);
@@ -790,19 +817,16 @@ fn invalid_blinded_path_error() {
let nodes = create_nodes(3);
let test_msg = TestCustomMessage::Pong;
- let secp_ctx = Secp256k1::new();
let intermediate_nodes =
[MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[2].entropy_source;
- let receive_key = nodes[2].messenger.node_signer.get_receive_auth_key();
let mut blinded_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[2].node_id,
- receive_key,
- context,
- entropy,
- &secp_ctx,
+ nodes[2].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[2].entropy_source,
+ &Secp256k1::new(),
);
blinded_path.clear_blinded_hops();
let destination = Destination::BlindedPath(blinded_path);
@@ -828,15 +852,13 @@ fn reply_path() {
MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[0].entropy_source;
- let receive_key = nodes[0].messenger.node_signer.get_receive_auth_key();
let reply_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[0].node_id,
- receive_key,
- context,
- entropy,
+ nodes[0].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[0].entropy_source,
&secp_ctx,
);
nodes[0]
@@ -855,15 +877,13 @@ fn reply_path() {
MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[3].entropy_source;
- let receive_key = nodes[3].messenger.node_signer.get_receive_auth_key();
let blinded_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[3].node_id,
- receive_key,
- context,
- entropy,
+ nodes[3].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[3].entropy_source,
&secp_ctx,
);
let destination = Destination::BlindedPath(blinded_path);
@@ -871,15 +891,13 @@ fn reply_path() {
MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[0].entropy_source;
- let receive_key = nodes[0].messenger.node_signer.get_receive_auth_key();
let reply_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[0].node_id,
- receive_key,
- context,
- entropy,
+ nodes[0].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[0].entropy_source,
&secp_ctx,
);
let instructions = MessageSendInstructions::WithSpecifiedReplyPath { destination, reply_path };
@@ -975,15 +993,13 @@ fn requests_peer_connection_for_buffered_messages() {
let intermediate_nodes =
[MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[0].entropy_source;
- let receive_key = nodes[0].messenger.node_signer.get_receive_auth_key();
let blinded_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[2].node_id,
- receive_key,
- context,
- entropy,
+ nodes[0].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[0].entropy_source,
&secp_ctx,
);
let destination = Destination::BlindedPath(blinded_path);
@@ -1046,15 +1062,13 @@ fn drops_buffered_messages_waiting_for_peer_connection() {
let intermediate_nodes =
[MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[0].entropy_source;
- let receive_key = nodes[0].messenger.node_signer.get_receive_auth_key();
let blinded_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[2].node_id,
- receive_key,
- context,
- entropy,
+ nodes[0].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[0].entropy_source,
&secp_ctx,
);
let destination = Destination::BlindedPath(blinded_path);
@@ -1107,19 +1121,16 @@ fn intercept_offline_peer_oms() {
}
let message = TestCustomMessage::Pong;
- let secp_ctx = Secp256k1::new();
let intermediate_nodes =
[MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
- let context = MessageContext::Custom(Vec::new());
- let entropy = &*nodes[2].entropy_source;
- let receive_key = nodes[2].messenger.node_signer.get_receive_auth_key();
let blinded_path = BlindedMessagePath::new(
&intermediate_nodes,
nodes[2].node_id,
- receive_key,
- context,
- entropy,
- &secp_ctx,
+ nodes[2].messenger.node_signer.get_receive_auth_key(),
+ MessageContext::Custom(Vec::new()),
+ false,
+ &*nodes[2].entropy_source,
+ &Secp256k1::new(),
);
let destination = Destination::BlindedPath(blinded_path);
let instructions = MessageSendInstructions::WithoutReplyPath { destination };
diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs
index 7de55cd..a61abae 100644
--- a/lightning/src/onion_message/messenger.rs
+++ b/lightning/src/onion_message/messenger.rs
@@ -272,7 +272,7 @@ where
/// ];
/// let context = MessageContext::Custom(Vec::new());
/// let receive_key = keys_manager.get_receive_auth_key();
-/// let blinded_path = BlindedMessagePath::new(&hops, your_node_id, receive_key, context, &keys_manager, &secp_ctx);
+/// let blinded_path = BlindedMessagePath::new(&hops, your_node_id, receive_key, context, false, &keys_manager, &secp_ctx);
///
/// // Send a custom onion message to a blinded path.
/// let destination = Destination::BlindedPath(blinded_path);
@@ -598,12 +598,12 @@ where
let is_recipient_announced =
network_graph.nodes().contains_key(&NodeId::from_pubkey(&recipient));
- let (mut compact_paths, dummy_hopd_path_len) = match &context {
+ let (size_constrained, path_len_incl_dummys) = match &context {
MessageContext::Offers(OffersContext::InvoiceRequest { .. })
| MessageContext::Offers(OffersContext::OutboundPaymentForRefund { .. }) => {
- // When embedding blinded paths within BOLT 12 objects which are generally embedded
- // in QR codes, we sadly need to be conservative about size, especially if the QR
- // code ultimately also includes an on-chain address.
+ // When including blinded paths within BOLT 12 objects that appear in QR codes, we
+ // sadly need to be conservative about size, especially if the QR code ultimately
+ // also includes an on-chain address.
(true, QR_CODED_DUMMY_HOPS_PATH_LENGTH)
},
MessageContext::Offers(OffersContext::StaticInvoiceRequested { .. }) => {
@@ -621,9 +621,7 @@ where
},
};
- if never_compact_path {
- compact_paths = false;
- }
+ let compact_paths = !never_compact_path && size_constrained;
let has_one_peer = peers.len() == 1;
let mut peer_info = peers
@@ -653,7 +651,7 @@ where
let build_path = |intermediate_hops: &[MessageForwardNode]| {
// Calculate the dummy hops given the total hop count target (including the recipient).
- let dummy_hops_count = dummy_hopd_path_len.saturating_sub(intermediate_hops.len() + 1);
+ let dummy_hops_count = path_len_incl_dummys.saturating_sub(intermediate_hops.len() + 1);
BlindedMessagePath::new_with_dummy_hops(
intermediate_hops,
@@ -661,6 +659,7 @@ where
dummy_hops_count,
local_node_receive_key,
context.clone(),
+ size_constrained,
&**entropy_source,
secp_ctx,
)
Why this scored 51/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.