Skip to content

Commit 133e6b6

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 cb2909e commit 133e6b6

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
@@ -223,7 +223,7 @@ fn two_unblinded_two_blinded() {
223223
let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response);
224224

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

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

238238
let secp_ctx = Secp256k1::new();
239-
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();
239+
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();
240240

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

265265
let secp_ctx = Secp256k1::new();
266-
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();
266+
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();
267267

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

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

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

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

307307
// Destination::Node
308-
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();
308+
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();
309309
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();
310310
nodes[3].custom_handler.expect_message(TestCustomMessage::Request);
311311
pass_along_path(&nodes);
@@ -315,8 +315,8 @@ fn reply_path() {
315315
pass_along_path(&nodes);
316316

317317
// Destination::BlindedPath
318-
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();
319-
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();
318+
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();
319+
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();
320320

321321
nodes[0].messenger.send_onion_message(&[], Destination::BlindedPath(blinded_path), OnionMessageContents::Custom(test_msg), Some(reply_path)).unwrap();
322322
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)