Skip to content

Commit 73202a1

Browse files
committed
Support NextHop::ShortChannelId in BlindedPath
When sending an onion message to a blinded path, the short channel id between hops isn't need in each hop's encrypted_payload since it is not a payment. However, using the short channel id instead of the node id gives a more compact representation. Update BlindedPath::new_for_message to allow for this.
1 parent 2c0fcf2 commit 73202a1

File tree

4 files changed

+87
-28
lines changed

4 files changed

+87
-28
lines changed

lightning/src/blinded_path/message.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
//! Data structures and methods for constructing [`BlindedPath`]s to send a message over.
2+
//!
3+
//! [`BlindedPath`]: crate::blinded_path::BlindedPath
4+
15
use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
26

37
#[allow(unused_imports)]
@@ -16,6 +20,16 @@ use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Writeable, Writer}
1620
use core::mem;
1721
use core::ops::Deref;
1822

23+
/// An intermediate node, and possibly a short channel id leading to the next node.
24+
pub struct ForwardNode {
25+
/// This node's pubkey.
26+
pub node_id: PublicKey,
27+
/// The channel between `node_id` and the next hop. If set, the constructed [`BlindedHop`]'s
28+
/// `encrypted_payload` will use this instead of the next [`ForwardNode::node_id`] for a more
29+
/// compact representation.
30+
pub short_channel_id: Option<u64>,
31+
}
32+
1933
/// TLVs to encode in an intermediate onion message packet's hop data. When provided in a blinded
2034
/// route, they are encoded into [`BlindedHop::encrypted_payload`].
2135
pub(crate) struct ForwardTlvs {
@@ -69,17 +83,24 @@ impl Writeable for ReceiveTlvs {
6983
}
7084
}
7185

72-
/// Construct blinded onion message hops for the given `unblinded_path`.
86+
/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
7387
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
74-
secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], session_priv: &SecretKey
88+
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[ForwardNode], recipient_node_id: PublicKey,
89+
session_priv: &SecretKey
7590
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
76-
let blinded_tlvs = unblinded_path.iter()
91+
let pks = intermediate_nodes.iter().map(|node| &node.node_id)
92+
.chain(core::iter::once(&recipient_node_id));
93+
let tlvs = pks.clone()
7794
.skip(1) // The first node's TLVs contains the next node's pubkey
78-
.map(|pk| ForwardTlvs { next_hop: NextHop::NodeId(*pk), next_blinding_override: None })
79-
.map(|tlvs| ControlTlvs::Forward(tlvs))
95+
.zip(intermediate_nodes.iter().map(|node| node.short_channel_id))
96+
.map(|(pubkey, scid)| match scid {
97+
Some(scid) => NextHop::ShortChannelId(scid),
98+
None => NextHop::NodeId(*pubkey),
99+
})
100+
.map(|next_hop| ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None }))
80101
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None })));
81102

82-
utils::construct_blinded_hops(secp_ctx, unblinded_path.iter(), blinded_tlvs, session_priv)
103+
utils::construct_blinded_hops(secp_ctx, pks, tlvs, session_priv)
83104
}
84105

85106
// Advance the blinded onion message path by one hop, so make the second hop into the new

lightning/src/blinded_path/mod.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//! Creating blinded paths and related utilities live here.
1111
1212
pub mod payment;
13-
pub(crate) mod message;
13+
pub mod message;
1414
pub(crate) mod utils;
1515

1616
use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
@@ -107,7 +107,7 @@ impl BlindedPath {
107107
pub fn one_hop_for_message<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
108108
recipient_node_id: PublicKey, entropy_source: &ES, secp_ctx: &Secp256k1<T>
109109
) -> Result<Self, ()> {
110-
Self::new_for_message(&[recipient_node_id], entropy_source, secp_ctx)
110+
Self::new_for_message(&[], recipient_node_id, entropy_source, secp_ctx)
111111
}
112112

113113
/// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node
@@ -116,17 +116,21 @@ impl BlindedPath {
116116
/// Errors if no hops are provided or if `node_pk`(s) are invalid.
117117
// TODO: make all payloads the same size with padding + add dummy hops
118118
pub fn new_for_message<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
119-
node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>
119+
intermediate_nodes: &[message::ForwardNode], recipient_node_id: PublicKey,
120+
entropy_source: &ES, secp_ctx: &Secp256k1<T>,
120121
) -> Result<Self, ()> {
121-
if node_pks.is_empty() { return Err(()) }
122+
let introduction_node = IntroductionNode::NodeId(
123+
intermediate_nodes.first().map_or(recipient_node_id, |n| n.node_id)
124+
);
122125
let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
123126
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
124-
let introduction_node = IntroductionNode::NodeId(node_pks[0]);
125127

126128
Ok(BlindedPath {
127129
introduction_node,
128130
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
129-
blinded_hops: message::blinded_hops(secp_ctx, node_pks, &blinding_secret).map_err(|_| ())?,
131+
blinded_hops: message::blinded_hops(
132+
secp_ctx, intermediate_nodes, recipient_node_id, &blinding_secret,
133+
).map_err(|_| ())?,
130134
})
131135
}
132136

lightning/src/onion_message/functional_tests.rs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//! Onion message testing and test utilities live here.
1111
1212
use crate::blinded_path::{BlindedPath, EmptyNodeIdLookUp};
13+
use crate::blinded_path::message::ForwardNode;
1314
use crate::events::{Event, EventsProvider};
1415
use crate::ln::features::{ChannelFeatures, InitFeatures};
1516
use crate::ln::msgs::{self, DecodeError, OnionMessageHandler};
@@ -280,7 +281,7 @@ fn one_blinded_hop() {
280281
let test_msg = TestCustomMessage::Response;
281282

282283
let secp_ctx = Secp256k1::new();
283-
let blinded_path = BlindedPath::new_for_message(&[nodes[1].node_id], &*nodes[1].entropy_source, &secp_ctx).unwrap();
284+
let blinded_path = BlindedPath::new_for_message(&[], nodes[1].node_id, &*nodes[1].entropy_source, &secp_ctx).unwrap();
284285
let destination = Destination::BlindedPath(blinded_path);
285286
nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap();
286287
nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response);
@@ -293,7 +294,8 @@ fn two_unblinded_two_blinded() {
293294
let test_msg = TestCustomMessage::Response;
294295

295296
let secp_ctx = Secp256k1::new();
296-
let blinded_path = BlindedPath::new_for_message(&[nodes[3].node_id, nodes[4].node_id], &*nodes[4].entropy_source, &secp_ctx).unwrap();
297+
let intermediate_nodes = [ForwardNode { node_id: nodes[3].node_id, short_channel_id: None }];
298+
let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[4].node_id, &*nodes[4].entropy_source, &secp_ctx).unwrap();
297299
let path = OnionMessagePath {
298300
intermediate_nodes: vec![nodes[1].node_id, nodes[2].node_id],
299301
destination: Destination::BlindedPath(blinded_path),
@@ -311,7 +313,11 @@ fn three_blinded_hops() {
311313
let test_msg = TestCustomMessage::Response;
312314

313315
let secp_ctx = Secp256k1::new();
314-
let blinded_path = BlindedPath::new_for_message(&[nodes[1].node_id, nodes[2].node_id, nodes[3].node_id], &*nodes[3].entropy_source, &secp_ctx).unwrap();
316+
let intermediate_nodes = [
317+
ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
318+
ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
319+
];
320+
let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[3].node_id, &*nodes[3].entropy_source, &secp_ctx).unwrap();
315321
let destination = Destination::BlindedPath(blinded_path);
316322

317323
nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap();
@@ -344,15 +350,20 @@ fn we_are_intro_node() {
344350
let test_msg = TestCustomMessage::Response;
345351

346352
let secp_ctx = Secp256k1::new();
347-
let blinded_path = BlindedPath::new_for_message(&[nodes[0].node_id, nodes[1].node_id, nodes[2].node_id], &*nodes[2].entropy_source, &secp_ctx).unwrap();
353+
let intermediate_nodes = [
354+
ForwardNode { node_id: nodes[0].node_id, short_channel_id: None },
355+
ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
356+
];
357+
let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx).unwrap();
348358
let destination = Destination::BlindedPath(blinded_path);
349359

350360
nodes[0].messenger.send_onion_message(test_msg.clone(), destination, None).unwrap();
351361
nodes[2].custom_message_handler.expect_message(TestCustomMessage::Response);
352362
pass_along_path(&nodes);
353363

354364
// Try with a two-hop blinded path where we are the introduction node.
355-
let blinded_path = BlindedPath::new_for_message(&[nodes[0].node_id, nodes[1].node_id], &*nodes[1].entropy_source, &secp_ctx).unwrap();
365+
let intermediate_nodes = [ForwardNode { node_id: nodes[0].node_id, short_channel_id: None }];
366+
let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[1].node_id, &*nodes[1].entropy_source, &secp_ctx).unwrap();
356367
let destination = Destination::BlindedPath(blinded_path);
357368
nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap();
358369
nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response);
@@ -368,7 +379,8 @@ fn invalid_blinded_path_error() {
368379

369380
// 0 hops
370381
let secp_ctx = Secp256k1::new();
371-
let mut blinded_path = BlindedPath::new_for_message(&[nodes[1].node_id, nodes[2].node_id], &*nodes[2].entropy_source, &secp_ctx).unwrap();
382+
let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
383+
let mut blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx).unwrap();
372384
blinded_path.blinded_hops.clear();
373385
let destination = Destination::BlindedPath(blinded_path);
374386
let err = nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap_err();
@@ -387,7 +399,11 @@ fn reply_path() {
387399
destination: Destination::Node(nodes[3].node_id),
388400
first_node_addresses: None,
389401
};
390-
let reply_path = BlindedPath::new_for_message(&[nodes[2].node_id, nodes[1].node_id, nodes[0].node_id], &*nodes[0].entropy_source, &secp_ctx).unwrap();
402+
let intermediate_nodes = [
403+
ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
404+
ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
405+
];
406+
let reply_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[0].node_id, &*nodes[0].entropy_source, &secp_ctx).unwrap();
391407
nodes[0].messenger.send_onion_message_using_path(path, test_msg.clone(), Some(reply_path)).unwrap();
392408
nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request);
393409
pass_along_path(&nodes);
@@ -397,9 +413,17 @@ fn reply_path() {
397413
pass_along_path(&nodes);
398414

399415
// Destination::BlindedPath
400-
let blinded_path = BlindedPath::new_for_message(&[nodes[1].node_id, nodes[2].node_id, nodes[3].node_id], &*nodes[3].entropy_source, &secp_ctx).unwrap();
416+
let intermediate_nodes = [
417+
ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
418+
ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
419+
];
420+
let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[3].node_id, &*nodes[3].entropy_source, &secp_ctx).unwrap();
401421
let destination = Destination::BlindedPath(blinded_path);
402-
let reply_path = BlindedPath::new_for_message(&[nodes[2].node_id, nodes[1].node_id, nodes[0].node_id], &*nodes[0].entropy_source, &secp_ctx).unwrap();
422+
let intermediate_nodes = [
423+
ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
424+
ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
425+
];
426+
let reply_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[0].node_id, &*nodes[0].entropy_source, &secp_ctx).unwrap();
403427

404428
nodes[0].messenger.send_onion_message(test_msg, destination, Some(reply_path)).unwrap();
405429
nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request);
@@ -476,8 +500,9 @@ fn requests_peer_connection_for_buffered_messages() {
476500
let secp_ctx = Secp256k1::new();
477501
add_channel_to_graph(&nodes[0], &nodes[1], &secp_ctx, 42);
478502

503+
let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
479504
let blinded_path = BlindedPath::new_for_message(
480-
&[nodes[1].node_id, nodes[2].node_id], &*nodes[0].entropy_source, &secp_ctx
505+
&intermediate_nodes, nodes[2].node_id, &*nodes[0].entropy_source, &secp_ctx
481506
).unwrap();
482507
let destination = Destination::BlindedPath(blinded_path);
483508

@@ -513,8 +538,9 @@ fn drops_buffered_messages_waiting_for_peer_connection() {
513538
let secp_ctx = Secp256k1::new();
514539
add_channel_to_graph(&nodes[0], &nodes[1], &secp_ctx, 42);
515540

541+
let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
516542
let blinded_path = BlindedPath::new_for_message(
517-
&[nodes[1].node_id, nodes[2].node_id], &*nodes[0].entropy_source, &secp_ctx
543+
&intermediate_nodes, nodes[2].node_id, &*nodes[0].entropy_source, &secp_ctx
518544
).unwrap();
519545
let destination = Destination::BlindedPath(blinded_path);
520546

lightning/src/onion_message/messenger.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use bitcoin::hashes::sha256::Hash as Sha256;
1616
use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};
1717

1818
use crate::blinded_path::{BlindedPath, IntroductionNode, NodeIdLookUp};
19-
use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, NextHop, ReceiveTlvs};
19+
use crate::blinded_path::message::{advance_path_by_one, ForwardNode, ForwardTlvs, NextHop, ReceiveTlvs};
2020
use crate::blinded_path::utils;
2121
use crate::events::{Event, EventHandler, EventsProvider};
2222
use crate::sign::{EntropySource, NodeSigner, Recipient};
@@ -71,6 +71,7 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
7171
/// # use bitcoin::hashes::hex::FromHex;
7272
/// # use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey, self};
7373
/// # use lightning::blinded_path::{BlindedPath, EmptyNodeIdLookUp};
74+
/// # use lightning::blinded_path::message::ForwardNode;
7475
/// # use lightning::sign::{EntropySource, KeysManager};
7576
/// # use lightning::ln::peer_handler::IgnoringMessageHandler;
7677
/// # use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath, OnionMessenger};
@@ -144,8 +145,11 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
144145
///
145146
/// // Create a blinded path to yourself, for someone to send an onion message to.
146147
/// # let your_node_id = hop_node_id1;
147-
/// let hops = [hop_node_id3, hop_node_id4, your_node_id];
148-
/// let blinded_path = BlindedPath::new_for_message(&hops, &keys_manager, &secp_ctx).unwrap();
148+
/// let hops = [
149+
/// ForwardNode { node_id: hop_node_id3, short_channel_id: None },
150+
/// ForwardNode { node_id: hop_node_id4, short_channel_id: None },
151+
/// ];
152+
/// let blinded_path = BlindedPath::new_for_message(&hops, your_node_id, &keys_manager, &secp_ctx).unwrap();
149153
///
150154
/// // Send a custom onion message to a blinded path.
151155
/// let destination = Destination::BlindedPath(blinded_path);
@@ -388,8 +392,12 @@ where
388392
});
389393

390394
let paths = peer_info.into_iter()
391-
.map(|(pubkey, _, _)| vec![pubkey, recipient])
392-
.map(|node_pks| BlindedPath::new_for_message(&node_pks, &*self.entropy_source, secp_ctx))
395+
.map(|(node_id, _, _)| vec![ForwardNode { node_id, short_channel_id: None }])
396+
.map(|intermediate_nodes| {
397+
BlindedPath::new_for_message(
398+
&intermediate_nodes, recipient, &*self.entropy_source, secp_ctx
399+
)
400+
})
393401
.take(MAX_PATHS)
394402
.collect::<Result<Vec<_>, _>>();
395403

0 commit comments

Comments
 (0)