Skip to content

Commit 70d8271

Browse files
Add blinded path {metadata} fields to Path
Lays groundwork to support routing, forwarding, and receiving to blinded paths TODO: squash upcoming commits into this one before merge, otherwise scoring and other things break
1 parent 97a8a80 commit 70d8271

File tree

12 files changed

+89
-46
lines changed

12 files changed

+89
-46
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p
359359
channel_features: dest.channel_features(),
360360
fee_msat: amt,
361361
cltv_expiry_delta: 200,
362-
}]}],
362+
}], blinded_tail: None }],
363363
payment_params: None,
364364
}, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) {
365365
check_payment_err(err);
@@ -388,7 +388,7 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des
388388
channel_features: dest.channel_features(),
389389
fee_msat: amt,
390390
cltv_expiry_delta: 200,
391-
}]}],
391+
}], blinded_tail: None }],
392392
payment_params: None,
393393
}, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) {
394394
check_payment_err(err);

lightning-background-processor/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1512,7 +1512,7 @@ mod tests {
15121512
channel_features: ChannelFeatures::empty(),
15131513
fee_msat: 0,
15141514
cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA as u32,
1515-
}]};
1515+
}], blinded_tail: None };
15161516

15171517
$nodes[0].scorer.lock().unwrap().expect(TestResult::PaymentFailure { path: path.clone(), short_channel_id: scored_scid });
15181518
$nodes[0].node.push_pending_event(Event::PaymentPathFailed {

lightning/src/events/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub enum ClosureReason {
129129
/// Be careful about printing the peer_msg, a well-crafted message could exploit
130130
/// a security vulnerability in the terminal emulator or the logging subsystem.
131131
/// To be safe, use `Display` on `UntrustedString`
132-
///
132+
///
133133
/// [`UntrustedString`]: crate::util::string::UntrustedString
134134
peer_msg: UntrustedString,
135135
},
@@ -1126,7 +1126,7 @@ impl MaybeReadable for Event {
11261126
payment_hash,
11271127
payment_failed_permanently,
11281128
failure,
1129-
path: Path { hops: path.unwrap() },
1129+
path: Path { hops: path.unwrap(), blinded_tail: None },
11301130
short_channel_id,
11311131
#[cfg(test)]
11321132
error_code,
@@ -1240,7 +1240,7 @@ impl MaybeReadable for Event {
12401240
Ok(Some(Event::PaymentPathSuccessful {
12411241
payment_id,
12421242
payment_hash,
1243-
path: Path { hops: path.unwrap() },
1243+
path: Path { hops: path.unwrap(), blinded_tail: None },
12441244
}))
12451245
};
12461246
f()
@@ -1301,7 +1301,7 @@ impl MaybeReadable for Event {
13011301
Ok(Some(Event::ProbeSuccessful {
13021302
payment_id,
13031303
payment_hash,
1304-
path: Path { hops: path.unwrap() },
1304+
path: Path { hops: path.unwrap(), blinded_tail: None },
13051305
}))
13061306
};
13071307
f()
@@ -1321,7 +1321,7 @@ impl MaybeReadable for Event {
13211321
Ok(Some(Event::ProbeFailed {
13221322
payment_id,
13231323
payment_hash,
1324-
path: Path { hops: path.unwrap() },
1324+
path: Path { hops: path.unwrap(), blinded_tail: None },
13251325
short_channel_id,
13261326
}))
13271327
};

lightning/src/ln/channel.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7191,7 +7191,7 @@ mod tests {
71917191
cltv_expiry: 200000000,
71927192
state: OutboundHTLCState::Committed,
71937193
source: HTLCSource::OutboundRoute {
7194-
path: Path { hops: Vec::new() },
7194+
path: Path { hops: Vec::new(), blinded_tail: None },
71957195
session_priv: SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
71967196
first_hop_htlc_msat: 548,
71977197
payment_id: PaymentId([42; 32]),

lightning/src/ln/channelmanager.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6901,7 +6901,7 @@ impl Readable for HTLCSource {
69016901
Ok(HTLCSource::OutboundRoute {
69026902
session_priv: session_priv.0.unwrap(),
69036903
first_hop_htlc_msat,
6904-
path: Path { hops: path },
6904+
path: Path { hops: path, blinded_tail: None },
69056905
payment_id: payment_id.unwrap(),
69066906
})
69076907
}

lightning/src/ln/functional_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,7 @@ fn fake_network_test() {
10441044
});
10451045
hops[1].fee_msat = chan_4.1.contents.fee_base_msat as u64 + chan_4.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
10461046
hops[0].fee_msat = chan_3.0.contents.fee_base_msat as u64 + chan_3.0.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
1047-
let payment_preimage_1 = send_along_route(&nodes[1], Route { paths: vec![Path { hops }], payment_params: None }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
1047+
let payment_preimage_1 = send_along_route(&nodes[1], Route { paths: vec![Path { hops, blinded_tail: None }], payment_params: None }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
10481048

10491049
let mut hops = Vec::with_capacity(3);
10501050
hops.push(RouteHop {
@@ -1073,7 +1073,7 @@ fn fake_network_test() {
10731073
});
10741074
hops[1].fee_msat = chan_2.1.contents.fee_base_msat as u64 + chan_2.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
10751075
hops[0].fee_msat = chan_3.1.contents.fee_base_msat as u64 + chan_3.1.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
1076-
let payment_hash_2 = send_along_route(&nodes[1], Route { paths: vec![Path { hops }], payment_params: None }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
1076+
let payment_hash_2 = send_along_route(&nodes[1], Route { paths: vec![Path { hops, blinded_tail: None }], payment_params: None }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
10771077

10781078
// Claim the rebalances...
10791079
fail_payment(&nodes[1], &vec!(&nodes[3], &nodes[2], &nodes[1])[..], payment_hash_2);

lightning/src/ln/onion_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,7 @@ mod tests {
928928
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
929929
short_channel_id: 0, fee_msat: 0, cltv_expiry_delta: 0 // We fill in the payloads manually instead of generating them from RouteHops.
930930
},
931-
]}],
931+
], blinded_tail: None }],
932932
payment_params: None,
933933
};
934934

lightning/src/ln/outbound_payment.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1506,7 +1506,7 @@ mod tests {
15061506
channel_features: ChannelFeatures::empty(),
15071507
fee_msat: 0,
15081508
cltv_expiry_delta: 0,
1509-
}]}],
1509+
}], blinded_tail: None }],
15101510
payment_params: Some(payment_params),
15111511
};
15121512
router.expect_find_route(route_params.clone(), Ok(route.clone()));

lightning/src/ln/payment_tests.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,15 +1844,15 @@ fn auto_retry_partial_failure() {
18441844
channel_features: nodes[1].node.channel_features(),
18451845
fee_msat: amt_msat / 2,
18461846
cltv_expiry_delta: 100,
1847-
}]},
1847+
}], blinded_tail: None },
18481848
Path { hops: vec![RouteHop {
18491849
pubkey: nodes[1].node.get_our_node_id(),
18501850
node_features: nodes[1].node.node_features(),
18511851
short_channel_id: chan_2_id,
18521852
channel_features: nodes[1].node.channel_features(),
18531853
fee_msat: amt_msat / 2,
18541854
cltv_expiry_delta: 100,
1855-
}]},
1855+
}], blinded_tail: None },
18561856
],
18571857
payment_params: Some(route_params.payment_params.clone()),
18581858
};
@@ -1865,15 +1865,15 @@ fn auto_retry_partial_failure() {
18651865
channel_features: nodes[1].node.channel_features(),
18661866
fee_msat: amt_msat / 4,
18671867
cltv_expiry_delta: 100,
1868-
}]},
1868+
}], blinded_tail: None },
18691869
Path { hops: vec![RouteHop {
18701870
pubkey: nodes[1].node.get_our_node_id(),
18711871
node_features: nodes[1].node.node_features(),
18721872
short_channel_id: chan_3_id,
18731873
channel_features: nodes[1].node.channel_features(),
18741874
fee_msat: amt_msat / 4,
18751875
cltv_expiry_delta: 100,
1876-
}]},
1876+
}], blinded_tail: None },
18771877
],
18781878
payment_params: Some(route_params.payment_params.clone()),
18791879
};
@@ -1886,7 +1886,7 @@ fn auto_retry_partial_failure() {
18861886
channel_features: nodes[1].node.channel_features(),
18871887
fee_msat: amt_msat / 4,
18881888
cltv_expiry_delta: 100,
1889-
}]},
1889+
}], blinded_tail: None },
18901890
],
18911891
payment_params: Some(route_params.payment_params.clone()),
18921892
};
@@ -2133,15 +2133,15 @@ fn retry_multi_path_single_failed_payment() {
21332133
channel_features: nodes[1].node.channel_features(),
21342134
fee_msat: 10_000,
21352135
cltv_expiry_delta: 100,
2136-
}]},
2136+
}], blinded_tail: None },
21372137
Path { hops: vec![RouteHop {
21382138
pubkey: nodes[1].node.get_our_node_id(),
21392139
node_features: nodes[1].node.node_features(),
21402140
short_channel_id: chans[1].short_channel_id.unwrap(),
21412141
channel_features: nodes[1].node.channel_features(),
21422142
fee_msat: 100_000_001, // Our default max-HTLC-value is 10% of the channel value, which this is one more than
21432143
cltv_expiry_delta: 100,
2144-
}]},
2144+
}], blinded_tail: None },
21452145
],
21462146
payment_params: Some(payment_params),
21472147
};
@@ -2227,7 +2227,7 @@ fn immediate_retry_on_failure() {
22272227
channel_features: nodes[1].node.channel_features(),
22282228
fee_msat: 100_000_001, // Our default max-HTLC-value is 10% of the channel value, which this is one more than
22292229
cltv_expiry_delta: 100,
2230-
}]},
2230+
}], blinded_tail: None },
22312231
],
22322232
payment_params: Some(PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)),
22332233
};
@@ -2322,7 +2322,7 @@ fn no_extra_retries_on_back_to_back_fail() {
23222322
channel_features: nodes[2].node.channel_features(),
23232323
fee_msat: 100_000_000,
23242324
cltv_expiry_delta: 100,
2325-
}]},
2325+
}], blinded_tail: None },
23262326
Path { hops: vec![RouteHop {
23272327
pubkey: nodes[1].node.get_our_node_id(),
23282328
node_features: nodes[1].node.node_features(),
@@ -2337,7 +2337,7 @@ fn no_extra_retries_on_back_to_back_fail() {
23372337
channel_features: nodes[2].node.channel_features(),
23382338
fee_msat: 100_000_000,
23392339
cltv_expiry_delta: 100,
2340-
}]}
2340+
}], blinded_tail: None }
23412341
],
23422342
payment_params: Some(PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)),
23432343
};
@@ -2524,7 +2524,7 @@ fn test_simple_partial_retry() {
25242524
channel_features: nodes[2].node.channel_features(),
25252525
fee_msat: 100_000_000,
25262526
cltv_expiry_delta: 100,
2527-
}]},
2527+
}], blinded_tail: None },
25282528
Path { hops: vec![RouteHop {
25292529
pubkey: nodes[1].node.get_our_node_id(),
25302530
node_features: nodes[1].node.node_features(),
@@ -2539,7 +2539,7 @@ fn test_simple_partial_retry() {
25392539
channel_features: nodes[2].node.channel_features(),
25402540
fee_msat: 100_000_000,
25412541
cltv_expiry_delta: 100,
2542-
}]}
2542+
}], blinded_tail: None }
25432543
],
25442544
payment_params: Some(PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)),
25452545
};
@@ -2690,7 +2690,7 @@ fn test_threaded_payment_retries() {
26902690
channel_features: nodes[2].node.channel_features(),
26912691
fee_msat: amt_msat / 1000,
26922692
cltv_expiry_delta: 100,
2693-
}]},
2693+
}], blinded_tail: None },
26942694
Path { hops: vec![RouteHop {
26952695
pubkey: nodes[2].node.get_our_node_id(),
26962696
node_features: nodes[2].node.node_features(),
@@ -2705,7 +2705,7 @@ fn test_threaded_payment_retries() {
27052705
channel_features: nodes[3].node.channel_features(),
27062706
fee_msat: amt_msat - amt_msat / 1000,
27072707
cltv_expiry_delta: 100,
2708-
}]}
2708+
}], blinded_tail: None }
27092709
],
27102710
payment_params: Some(PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)),
27112711
};

lightning/src/routing/router.rs

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,16 @@ pub struct RouteHop {
226226
/// to reach this node.
227227
pub channel_features: ChannelFeatures,
228228
/// The fee taken on this hop (for paying for the use of the *next* channel in the path).
229-
/// For the last hop, this should be the full value of this path's part of the payment.
229+
/// If this is the last hop in [`Path::hops`] and we're not sending to a [`BlindedPath`], this
230+
/// is the full value of this [`Path`]'s part of the payment.
231+
///
232+
/// [`BlindedPath`]: crate::blinded_path::BlindedPath
230233
pub fee_msat: u64,
231-
/// The CLTV delta added for this hop. For the last hop, this should be the full CLTV value
232-
/// expected at the destination, in excess of the current block height.
234+
/// The CLTV delta added for this hop. If this is the last hop in [`Path::hops`] and we're not
235+
/// sending to a [`BlindedPath`], this is the full CLTV value expected at the destination, in
236+
/// excess of the current block height.
237+
///
238+
/// [`BlindedPath`]: crate::blinded_path::BlindedPath
233239
pub cltv_expiry_delta: u32,
234240
}
235241

@@ -242,22 +248,58 @@ impl_writeable_tlv_based!(RouteHop, {
242248
(10, cltv_expiry_delta, required),
243249
});
244250

251+
/// The blinded portion of a [`Path`], if we're routing to a recipient who provided blinded paths in
252+
/// their BOLT12 [`Invoice`].
253+
///
254+
/// [`Invoice`]: crate::offers::invoice::Invoice
255+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
256+
pub struct BlindedTail {
257+
/// The blinded path itself, provided by the recipient.
258+
pub path: BlindedPath,
259+
/// The total fee paid on this blinded path, calculated using the [`BlindedPayInfo`] provided in
260+
/// the BOLT 12 invoice.
261+
///
262+
/// [`BlindedPayInfo`]: crate::offers::invoice::BlindedPayInfo
263+
pub fee_msat: u64,
264+
/// Number of blocks subtracted from an incoming HTLC's `cltv_expiry` for the entire blinded path.
265+
pub cltv_expiry_delta: u32,
266+
/// The total amount paid on this [`Path`], excluding the fees.
267+
pub final_value_msat: u64,
268+
/// The scid used to reach the introduction node of the [`BlindedPath`].
269+
///
270+
/// [`BlindedPath`]: crate::blinded_path::BlindedPath
271+
pub intro_node_scid: u64,
272+
}
273+
274+
impl_writeable_tlv_based!(BlindedTail, {
275+
(0, path, required),
276+
(2, fee_msat, required),
277+
(4, cltv_expiry_delta, required),
278+
(6, final_value_msat, required),
279+
(8, intro_node_scid, required),
280+
});
281+
245282
/// A path in a [`Route`] to the payment recipient.
246283
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
247284
pub struct Path {
248-
/// The list of unblinded hops in this [`Path`].
285+
/// The list of unblinded hops in this [`Path`]. May be empty if a blinded path is present,
286+
/// otherwise the last hop is the destination and this must be at least length one.
249287
pub hops: Vec<RouteHop>,
288+
/// The blinded path at which this path terminates, if we're sending to one, and its metadata.
289+
pub blinded_tail: Option<BlindedTail>,
250290
}
251291

252292
/// A route directs a payment from the sender (us) to the recipient. If the recipient supports MPP,
253293
/// it can take multiple paths. Each path is composed of one or more hops through the network.
254294
#[derive(Clone, Hash, PartialEq, Eq)]
255295
pub struct Route {
256-
/// The list of paths taken for a single (potentially-)multi-part payment. The pubkey of the
257-
/// last [`RouteHop`] in each path must be the same. Each entry represents a list of hops, NOT
258-
/// INCLUDING our own, where the last hop is the destination. Thus, this must always be at
259-
/// least length one. While the maximum length of any given path is variable, keeping the length
260-
/// of any path less or equal to 19 should currently ensure it is viable.
296+
/// The list of [`Path`]s taken for a single (potentially-)multi-part payment. Each entry
297+
/// represents a list of hops, NOT INCLUDING our own, where the last hop is either the recipient
298+
/// or a [`BlindedTail`] that hides the recipient's identity. Thus, [`Path::len`] must always be
299+
/// at least length one. While the maximum length of any given path is variable, keeping the
300+
/// length of any path less than or equal to 19 should currently ensure it is viable. If no
301+
/// [`BlindedTail`]s are present, then the pubkey of the last [`RouteHop`] in each path must be
302+
/// the same.
261303
pub paths: Vec<Path>,
262304
/// The `payment_params` parameter passed to [`find_route`].
263305
/// This is used by `ChannelManager` to track information which may be required for retries,
@@ -334,7 +376,7 @@ impl Readable for Route {
334376
if hops.is_empty() { return Err(DecodeError::InvalidValue); }
335377
min_final_cltv_expiry_delta =
336378
cmp::min(min_final_cltv_expiry_delta, hops.last().unwrap().cltv_expiry_delta);
337-
paths.push(Path { hops });
379+
paths.push(Path { hops, blinded_tail: None });
338380
}
339381
let mut payment_params = None;
340382
read_tlv_fields!(reader, {
@@ -2015,7 +2057,7 @@ where L::Target: Logger {
20152057
for results_vec in selected_paths {
20162058
let mut hops = Vec::with_capacity(results_vec.len());
20172059
for res in results_vec { hops.push(res?); }
2018-
paths.push(Path { hops });
2060+
paths.push(Path { hops, blinded_tail: None });
20192061
}
20202062
let route = Route {
20212063
paths,
@@ -5266,7 +5308,7 @@ mod tests {
52665308
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
52675309
short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0
52685310
},
5269-
]}],
5311+
], blinded_tail: None }],
52705312
payment_params: None,
52715313
};
52725314

@@ -5288,7 +5330,7 @@ mod tests {
52885330
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
52895331
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
52905332
},
5291-
]}, Path { hops: vec![
5333+
], blinded_tail: None }, Path { hops: vec![
52925334
RouteHop {
52935335
pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
52945336
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
@@ -5299,7 +5341,7 @@ mod tests {
52995341
channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
53005342
short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
53015343
},
5302-
]}],
5344+
], blinded_tail: None }],
53035345
payment_params: None,
53045346
};
53055347

0 commit comments

Comments
 (0)