Skip to content

Commit aec266b

Browse files
committed
Option to set path_id in BlindedPath
The final hop in a BlindedPath may contain private data to provide context to the receiver, allowing them to verify that the path was used in desired context (e.g., making a payment). This data can be used in a similar manner to BOLT 11's payment secret, which may include data necessary to make inbound payments stateless (e.g., expiry, amount, preimage, etc.). Add separate constructors to BlindedPath for with and without the path id.
1 parent 11b7989 commit aec266b

File tree

5 files changed

+37
-20
lines changed

5 files changed

+37
-20
lines changed

fuzz/src/invoice_request_deser.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ fn build_response<'a, T: secp256k1::Signing + secp256k1::Verification>(
7474
) -> Result<UnsignedInvoice<'a>, SemanticError> {
7575
let entropy_source = Randomness {};
7676
let paths = vec![
77-
BlindedPath::new(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
78-
BlindedPath::new(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
77+
BlindedPath::without_id(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
78+
BlindedPath::without_id(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
7979
];
8080

8181
let payinfo = vec![

fuzz/src/refund_deser.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ fn build_response<'a, T: secp256k1::Signing + secp256k1::Verification>(
6363
) -> Result<UnsignedInvoice<'a>, SemanticError> {
6464
let entropy_source = Randomness {};
6565
let paths = vec![
66-
BlindedPath::new(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
67-
BlindedPath::new(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
66+
BlindedPath::without_id(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
67+
BlindedPath::without_id(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
6868
];
6969

7070
let payinfo = vec![

lightning/src/onion_message/blinded_path.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,30 @@ pub struct BlindedHop {
5858
}
5959

6060
impl BlindedPath {
61+
/// Create a blinded path to be forwarded along `node_pks`. The last node pubkey in `node_pks`
62+
/// will be the destination node and will receive the `path_id`.
63+
///
64+
/// Errors if less than two hops are provided or if `node_pk`(s) are invalid.
65+
pub fn with_id<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>(
66+
id: [u8; 32], node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>
67+
) -> Result<Self, ()> {
68+
Self::new(Some(id), node_pks, entropy_source, secp_ctx)
69+
}
70+
6171
/// Create a blinded path to be forwarded along `node_pks`. The last node pubkey in `node_pks`
6272
/// will be the destination node.
6373
///
6474
/// Errors if less than two hops are provided or if `node_pk`(s) are invalid.
75+
pub fn without_id<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>(
76+
node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>
77+
) -> Result<Self, ()> {
78+
Self::new(None, node_pks, entropy_source, secp_ctx)
79+
}
80+
6581
// TODO: make all payloads the same size with padding + add dummy hops
66-
pub fn new<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>
67-
(node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>) -> Result<Self, ()>
68-
{
82+
fn new<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>(
83+
id: Option<[u8; 32]>, node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>
84+
) -> Result<Self, ()> {
6985
if node_pks.len() < 2 { return Err(()) }
7086
let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
7187
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
@@ -74,7 +90,7 @@ impl BlindedPath {
7490
Ok(BlindedPath {
7591
introduction_node_id,
7692
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
77-
blinded_hops: blinded_hops(secp_ctx, node_pks, &blinding_secret).map_err(|_| ())?,
93+
blinded_hops: blinded_hops(secp_ctx, id, node_pks, &blinding_secret).map_err(|_| ())?,
7894
})
7995
}
8096

@@ -116,7 +132,8 @@ impl BlindedPath {
116132

117133
/// Construct blinded hops for the given `unblinded_path`.
118134
fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
119-
secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], session_priv: &SecretKey
135+
secp_ctx: &Secp256k1<T>, path_id: Option<[u8; 32]>, unblinded_path: &[PublicKey],
136+
session_priv: &SecretKey
120137
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
121138
let mut blinded_hops = Vec::with_capacity(unblinded_path.len());
122139

@@ -138,7 +155,7 @@ fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
138155
})?;
139156

140157
if let Some((final_ss, final_blinded_node_id)) = prev_ss_and_blinded_node_id {
141-
let final_payload = ReceiveTlvs { path_id: None };
158+
let final_payload = ReceiveTlvs { path_id };
142159
blinded_hops.push(BlindedHop {
143160
blinded_node_id: final_blinded_node_id,
144161
encrypted_payload: encrypt_payload(final_payload, final_ss),

lightning/src/onion_message/functional_tests.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ fn two_unblinded_two_blinded() {
222222
let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response);
223223

224224
let secp_ctx = Secp256k1::new();
225-
let blinded_path = BlindedPath::new(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap();
225+
let blinded_path = BlindedPath::without_id(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap();
226226

227227
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::BlindedPath(blinded_path), test_msg, None).unwrap();
228228
nodes[4].custom_handler.expect_message(TestCustomMessage::Response);
@@ -235,7 +235,7 @@ fn three_blinded_hops() {
235235
let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response);
236236

237237
let secp_ctx = Secp256k1::new();
238-
let blinded_path = BlindedPath::new(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();
238+
let blinded_path = BlindedPath::without_id(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();
239239

240240
nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), test_msg, None).unwrap();
241241
nodes[3].custom_handler.expect_message(TestCustomMessage::Response);
@@ -262,14 +262,14 @@ fn we_are_intro_node() {
262262
let test_msg = TestCustomMessage::Response;
263263

264264
let secp_ctx = Secp256k1::new();
265-
let blinded_path = BlindedPath::new(&[nodes[0].get_node_pk(), nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
265+
let blinded_path = BlindedPath::without_id(&[nodes[0].get_node_pk(), nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
266266

267267
nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), OnionMessageContents::Custom(test_msg.clone()), None).unwrap();
268268
nodes[2].custom_handler.expect_message(TestCustomMessage::Response);
269269
pass_along_path(&nodes);
270270

271271
// Try with a two-hop blinded path where we are the introduction node.
272-
let blinded_path = BlindedPath::new(&[nodes[0].get_node_pk(), nodes[1].get_node_pk()], &*nodes[1].keys_manager, &secp_ctx).unwrap();
272+
let blinded_path = BlindedPath::without_id(&[nodes[0].get_node_pk(), nodes[1].get_node_pk()], &*nodes[1].keys_manager, &secp_ctx).unwrap();
273273
nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), OnionMessageContents::Custom(test_msg), None).unwrap();
274274
nodes[1].custom_handler.expect_message(TestCustomMessage::Response);
275275
nodes.remove(2);
@@ -284,13 +284,13 @@ fn invalid_blinded_path_error() {
284284

285285
// 0 hops
286286
let secp_ctx = Secp256k1::new();
287-
let mut blinded_path = BlindedPath::new(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
287+
let mut blinded_path = BlindedPath::without_id(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
288288
blinded_path.blinded_hops.clear();
289289
let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), OnionMessageContents::Custom(test_msg.clone()), None).unwrap_err();
290290
assert_eq!(err, SendError::TooFewBlindedHops);
291291

292292
// 1 hop
293-
let mut blinded_path = BlindedPath::new(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
293+
let mut blinded_path = BlindedPath::without_id(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
294294
blinded_path.blinded_hops.remove(0);
295295
assert_eq!(blinded_path.blinded_hops.len(), 1);
296296
let err = nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), OnionMessageContents::Custom(test_msg), None).unwrap_err();
@@ -304,7 +304,7 @@ fn reply_path() {
304304
let secp_ctx = Secp256k1::new();
305305

306306
// Destination::Node
307-
let reply_path = BlindedPath::new(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
307+
let reply_path = BlindedPath::without_id(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
308308
nodes[0].messenger.send_onion_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], Destination::Node(nodes[3].get_node_pk()), OnionMessageContents::Custom(test_msg.clone()), Some(reply_path)).unwrap();
309309
nodes[3].custom_handler.expect_message(TestCustomMessage::Request);
310310
pass_along_path(&nodes);
@@ -314,8 +314,8 @@ fn reply_path() {
314314
pass_along_path(&nodes);
315315

316316
// Destination::BlindedPath
317-
let blinded_path = BlindedPath::new(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();
318-
let reply_path = BlindedPath::new(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
317+
let blinded_path = BlindedPath::without_id(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();
318+
let reply_path = BlindedPath::without_id(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
319319

320320
nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), OnionMessageContents::Custom(test_msg), Some(reply_path)).unwrap();
321321
nodes[3].custom_handler.expect_message(TestCustomMessage::Request);

lightning/src/onion_message/messenger.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ use crate::prelude::*;
104104
/// // Create a blinded path to yourself, for someone to send an onion message to.
105105
/// # let your_node_id = hop_node_id1;
106106
/// let hops = [hop_node_id3, hop_node_id4, your_node_id];
107-
/// let blinded_path = BlindedPath::new(&hops, &keys_manager, &secp_ctx).unwrap();
107+
/// let blinded_path = BlindedPath::without_id(&hops, &keys_manager, &secp_ctx).unwrap();
108108
///
109109
/// // Send a custom onion message to a blinded path.
110110
/// # let intermediate_hops = [hop_node_id1, hop_node_id2];

0 commit comments

Comments
 (0)