Skip to content

Commit 9984660

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 a95338a commit 9984660

File tree

6 files changed

+115
-33
lines changed

6 files changed

+115
-33
lines changed

fuzz/src/invoice_request_deser.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use bitcoin::secp256k1::{KeyPair, Parity, PublicKey, Secp256k1, SecretKey, self}
1111
use crate::utils::test_logger;
1212
use core::convert::TryFrom;
1313
use lightning::blinded_path::BlindedPath;
14+
use lightning::blinded_path::message::ForwardNode;
1415
use lightning::sign::EntropySource;
1516
use lightning::ln::PaymentHash;
1617
use lightning::ln::features::BlindedHopFeatures;
@@ -73,9 +74,19 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
7374
invoice_request: &InvoiceRequest, secp_ctx: &Secp256k1<T>
7475
) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
7576
let entropy_source = Randomness {};
77+
let intermediate_nodes = [
78+
[
79+
ForwardNode { node_id: pubkey(43), short_channel_id: None },
80+
ForwardNode { node_id: pubkey(44), short_channel_id: None },
81+
],
82+
[
83+
ForwardNode { node_id: pubkey(45), short_channel_id: None },
84+
ForwardNode { node_id: pubkey(46), short_channel_id: None },
85+
],
86+
];
7687
let paths = vec![
77-
BlindedPath::new_for_message(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
78-
BlindedPath::new_for_message(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
88+
BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx).unwrap(),
89+
BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx).unwrap(),
7990
];
8091

8192
let payinfo = vec![

fuzz/src/refund_deser.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey, self};
1111
use crate::utils::test_logger;
1212
use core::convert::TryFrom;
1313
use lightning::blinded_path::BlindedPath;
14+
use lightning::blinded_path::message::ForwardNode;
1415
use lightning::sign::EntropySource;
1516
use lightning::ln::PaymentHash;
1617
use lightning::ln::features::BlindedHopFeatures;
@@ -62,9 +63,19 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
6263
refund: &Refund, signing_pubkey: PublicKey, secp_ctx: &Secp256k1<T>
6364
) -> Result<UnsignedBolt12Invoice, Bolt12SemanticError> {
6465
let entropy_source = Randomness {};
66+
let intermediate_nodes = [
67+
[
68+
ForwardNode { node_id: pubkey(43), short_channel_id: None },
69+
ForwardNode { node_id: pubkey(44), short_channel_id: None },
70+
],
71+
[
72+
ForwardNode { node_id: pubkey(45), short_channel_id: None },
73+
ForwardNode { node_id: pubkey(46), short_channel_id: None },
74+
],
75+
];
6576
let paths = vec![
66-
BlindedPath::new_for_message(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
67-
BlindedPath::new_for_message(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
77+
BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx).unwrap(),
78+
BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx).unwrap(),
6879
];
6980

7081
let payinfo = vec![

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 {
@@ -60,17 +74,24 @@ impl Writeable for ReceiveTlvs {
6074
}
6175
}
6276

63-
/// Construct blinded onion message hops for the given `unblinded_path`.
77+
/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
6478
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
65-
secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], session_priv: &SecretKey
79+
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[ForwardNode], recipient_node_id: PublicKey,
80+
session_priv: &SecretKey
6681
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
67-
let blinded_tlvs = unblinded_path.iter()
82+
let pks = intermediate_nodes.iter().map(|node| &node.node_id)
83+
.chain(core::iter::once(&recipient_node_id));
84+
let tlvs = pks.clone()
6885
.skip(1) // The first node's TLVs contains the next node's pubkey
69-
.map(|pk| ForwardTlvs { next_hop: NextMessageHop::NodeId(*pk), next_blinding_override: None })
70-
.map(|tlvs| ControlTlvs::Forward(tlvs))
86+
.zip(intermediate_nodes.iter().map(|node| node.short_channel_id))
87+
.map(|(pubkey, scid)| match scid {
88+
Some(scid) => NextMessageHop::ShortChannelId(scid),
89+
None => NextMessageHop::NodeId(*pubkey),
90+
})
91+
.map(|next_hop| ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None }))
7192
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None })));
7293

73-
utils::construct_blinded_hops(secp_ctx, unblinded_path.iter(), blinded_tlvs, session_priv)
94+
utils::construct_blinded_hops(secp_ctx, pks, tlvs, session_priv)
7495
}
7596

7697
// 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};
@@ -124,7 +124,7 @@ impl BlindedPath {
124124
pub fn one_hop_for_message<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
125125
recipient_node_id: PublicKey, entropy_source: ES, secp_ctx: &Secp256k1<T>
126126
) -> Result<Self, ()> where ES::Target: EntropySource {
127-
Self::new_for_message(&[recipient_node_id], entropy_source, secp_ctx)
127+
Self::new_for_message(&[], recipient_node_id, entropy_source, secp_ctx)
128128
}
129129

130130
/// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node
@@ -133,17 +133,21 @@ impl BlindedPath {
133133
/// Errors if no hops are provided or if `node_pk`(s) are invalid.
134134
// TODO: make all payloads the same size with padding + add dummy hops
135135
pub fn new_for_message<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
136-
node_pks: &[PublicKey], entropy_source: ES, secp_ctx: &Secp256k1<T>
136+
intermediate_nodes: &[message::ForwardNode], recipient_node_id: PublicKey,
137+
entropy_source: ES, secp_ctx: &Secp256k1<T>
137138
) -> Result<Self, ()> where ES::Target: EntropySource {
138-
if node_pks.is_empty() { return Err(()) }
139+
let introduction_node = IntroductionNode::NodeId(
140+
intermediate_nodes.first().map_or(recipient_node_id, |n| n.node_id)
141+
);
139142
let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
140143
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
141-
let introduction_node = IntroductionNode::NodeId(node_pks[0]);
142144

143145
Ok(BlindedPath {
144146
introduction_node,
145147
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
146-
blinded_hops: message::blinded_hops(secp_ctx, node_pks, &blinding_secret).map_err(|_| ())?,
148+
blinded_hops: message::blinded_hops(
149+
secp_ctx, intermediate_nodes, recipient_node_id, &blinding_secret,
150+
).map_err(|_| ())?,
147151
})
148152
}
149153

lightning/src/onion_message/functional_tests.rs

Lines changed: 39 additions & 12 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};
@@ -327,7 +328,7 @@ fn one_blinded_hop() {
327328
let test_msg = TestCustomMessage::Response;
328329

329330
let secp_ctx = Secp256k1::new();
330-
let blinded_path = BlindedPath::new_for_message(&[nodes[1].node_id], &*nodes[1].entropy_source, &secp_ctx).unwrap();
331+
let blinded_path = BlindedPath::new_for_message(&[], nodes[1].node_id, &*nodes[1].entropy_source, &secp_ctx).unwrap();
331332
let destination = Destination::BlindedPath(blinded_path);
332333
nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap();
333334
nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response);
@@ -340,7 +341,8 @@ fn two_unblinded_two_blinded() {
340341
let test_msg = TestCustomMessage::Response;
341342

342343
let secp_ctx = Secp256k1::new();
343-
let blinded_path = BlindedPath::new_for_message(&[nodes[3].node_id, nodes[4].node_id], &*nodes[4].entropy_source, &secp_ctx).unwrap();
344+
let intermediate_nodes = [ForwardNode { node_id: nodes[3].node_id, short_channel_id: None }];
345+
let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[4].node_id, &*nodes[4].entropy_source, &secp_ctx).unwrap();
344346
let path = OnionMessagePath {
345347
intermediate_nodes: vec![nodes[1].node_id, nodes[2].node_id],
346348
destination: Destination::BlindedPath(blinded_path),
@@ -358,7 +360,11 @@ fn three_blinded_hops() {
358360
let test_msg = TestCustomMessage::Response;
359361

360362
let secp_ctx = Secp256k1::new();
361-
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();
363+
let intermediate_nodes = [
364+
ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
365+
ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
366+
];
367+
let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[3].node_id, &*nodes[3].entropy_source, &secp_ctx).unwrap();
362368
let destination = Destination::BlindedPath(blinded_path);
363369

364370
nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap();
@@ -391,15 +397,20 @@ fn we_are_intro_node() {
391397
let test_msg = TestCustomMessage::Response;
392398

393399
let secp_ctx = Secp256k1::new();
394-
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();
400+
let intermediate_nodes = [
401+
ForwardNode { node_id: nodes[0].node_id, short_channel_id: None },
402+
ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
403+
];
404+
let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx).unwrap();
395405
let destination = Destination::BlindedPath(blinded_path);
396406

397407
nodes[0].messenger.send_onion_message(test_msg.clone(), destination, None).unwrap();
398408
nodes[2].custom_message_handler.expect_message(TestCustomMessage::Response);
399409
pass_along_path(&nodes);
400410

401411
// Try with a two-hop blinded path where we are the introduction node.
402-
let blinded_path = BlindedPath::new_for_message(&[nodes[0].node_id, nodes[1].node_id], &*nodes[1].entropy_source, &secp_ctx).unwrap();
412+
let intermediate_nodes = [ForwardNode { node_id: nodes[0].node_id, short_channel_id: None }];
413+
let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[1].node_id, &*nodes[1].entropy_source, &secp_ctx).unwrap();
403414
let destination = Destination::BlindedPath(blinded_path);
404415
nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap();
405416
nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response);
@@ -414,7 +425,8 @@ fn invalid_blinded_path_error() {
414425
let test_msg = TestCustomMessage::Response;
415426

416427
let secp_ctx = Secp256k1::new();
417-
let mut blinded_path = BlindedPath::new_for_message(&[nodes[1].node_id, nodes[2].node_id], &*nodes[2].entropy_source, &secp_ctx).unwrap();
428+
let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
429+
let mut blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx).unwrap();
418430
blinded_path.blinded_hops.clear();
419431
let destination = Destination::BlindedPath(blinded_path);
420432
let err = nodes[0].messenger.send_onion_message(test_msg, destination, None).unwrap_err();
@@ -433,7 +445,11 @@ fn reply_path() {
433445
destination: Destination::Node(nodes[3].node_id),
434446
first_node_addresses: None,
435447
};
436-
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();
448+
let intermediate_nodes = [
449+
ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
450+
ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
451+
];
452+
let reply_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[0].node_id, &*nodes[0].entropy_source, &secp_ctx).unwrap();
437453
nodes[0].messenger.send_onion_message_using_path(path, test_msg.clone(), Some(reply_path)).unwrap();
438454
nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request);
439455
pass_along_path(&nodes);
@@ -443,9 +459,17 @@ fn reply_path() {
443459
pass_along_path(&nodes);
444460

445461
// Destination::BlindedPath
446-
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();
462+
let intermediate_nodes = [
463+
ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
464+
ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
465+
];
466+
let blinded_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[3].node_id, &*nodes[3].entropy_source, &secp_ctx).unwrap();
447467
let destination = Destination::BlindedPath(blinded_path);
448-
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();
468+
let intermediate_nodes = [
469+
ForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
470+
ForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
471+
];
472+
let reply_path = BlindedPath::new_for_message(&intermediate_nodes, nodes[0].node_id, &*nodes[0].entropy_source, &secp_ctx).unwrap();
449473

450474
nodes[0].messenger.send_onion_message(test_msg, destination, Some(reply_path)).unwrap();
451475
nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request);
@@ -525,8 +549,9 @@ fn requests_peer_connection_for_buffered_messages() {
525549
let secp_ctx = Secp256k1::new();
526550
add_channel_to_graph(&nodes[0], &nodes[1], &secp_ctx, 42);
527551

552+
let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
528553
let blinded_path = BlindedPath::new_for_message(
529-
&[nodes[1].node_id, nodes[2].node_id], &*nodes[0].entropy_source, &secp_ctx
554+
&intermediate_nodes, nodes[2].node_id, &*nodes[0].entropy_source, &secp_ctx
530555
).unwrap();
531556
let destination = Destination::BlindedPath(blinded_path);
532557

@@ -562,8 +587,9 @@ fn drops_buffered_messages_waiting_for_peer_connection() {
562587
let secp_ctx = Secp256k1::new();
563588
add_channel_to_graph(&nodes[0], &nodes[1], &secp_ctx, 42);
564589

590+
let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
565591
let blinded_path = BlindedPath::new_for_message(
566-
&[nodes[1].node_id, nodes[2].node_id], &*nodes[0].entropy_source, &secp_ctx
592+
&intermediate_nodes, nodes[2].node_id, &*nodes[0].entropy_source, &secp_ctx
567593
).unwrap();
568594
let destination = Destination::BlindedPath(blinded_path);
569595

@@ -611,8 +637,9 @@ fn intercept_offline_peer_oms() {
611637

612638
let message = TestCustomMessage::Response;
613639
let secp_ctx = Secp256k1::new();
640+
let intermediate_nodes = [ForwardNode { node_id: nodes[1].node_id, short_channel_id: None }];
614641
let blinded_path = BlindedPath::new_for_message(
615-
&[nodes[1].node_id, nodes[2].node_id], &*nodes[2].entropy_source, &secp_ctx
642+
&intermediate_nodes, nodes[2].node_id, &*nodes[2].entropy_source, &secp_ctx
616643
).unwrap();
617644
let destination = Destination::BlindedPath(blinded_path);
618645

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, NextMessageHop, NodeIdLookUp};
19-
use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, ReceiveTlvs};
19+
use crate::blinded_path::message::{advance_path_by_one, ForwardNode, ForwardTlvs, 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};
@@ -145,8 +146,11 @@ pub(super) const MAX_TIMER_TICKS: usize = 2;
145146
///
146147
/// // Create a blinded path to yourself, for someone to send an onion message to.
147148
/// # let your_node_id = hop_node_id1;
148-
/// let hops = [hop_node_id3, hop_node_id4, your_node_id];
149-
/// let blinded_path = BlindedPath::new_for_message(&hops, &keys_manager, &secp_ctx).unwrap();
149+
/// let hops = [
150+
/// ForwardNode { node_id: hop_node_id3, short_channel_id: None },
151+
/// ForwardNode { node_id: hop_node_id4, short_channel_id: None },
152+
/// ];
153+
/// let blinded_path = BlindedPath::new_for_message(&hops, your_node_id, &keys_manager, &secp_ctx).unwrap();
150154
///
151155
/// // Send a custom onion message to a blinded path.
152156
/// let destination = Destination::BlindedPath(blinded_path);
@@ -435,8 +439,12 @@ where
435439
});
436440

437441
let paths = peer_info.into_iter()
438-
.map(|(pubkey, _, _)| vec![pubkey, recipient])
439-
.map(|node_pks| BlindedPath::new_for_message(&node_pks, &*self.entropy_source, secp_ctx))
442+
.map(|(node_id, _, _)| vec![ForwardNode { node_id, short_channel_id: None }])
443+
.map(|intermediate_nodes| {
444+
BlindedPath::new_for_message(
445+
&intermediate_nodes, recipient, &*self.entropy_source, secp_ctx
446+
)
447+
})
440448
.take(MAX_PATHS)
441449
.collect::<Result<Vec<_>, _>>();
442450

0 commit comments

Comments
 (0)