Skip to content

Commit 7aba42b

Browse files
Add Path struct and use it in Route and HTLCSource
Note that this breaks scoring and inflight htlc tracking, which are fixed in upcoming commits
1 parent ccc98ba commit 7aba42b

17 files changed

+798
-694
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use lightning::util::errors::APIError;
5050
use lightning::util::logger::Logger;
5151
use lightning::util::config::UserConfig;
5252
use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer};
53-
use lightning::routing::router::{InFlightHtlcs, Route, RouteHop, RouteParameters, Router};
53+
use lightning::routing::router::{InFlightHtlcs, Path, Route, RouteHop, RouteParameters, Router};
5454

5555
use crate::utils::test_logger::{self, Output};
5656
use crate::utils::test_persister::TestPersister;
@@ -352,14 +352,14 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p
352352
payment_id[0..8].copy_from_slice(&payment_idx.to_ne_bytes());
353353
*payment_idx += 1;
354354
if let Err(err) = source.send_payment(&Route {
355-
paths: vec![vec![RouteHop {
355+
paths: vec![Path { hops: vec![RouteHop {
356356
pubkey: dest.get_our_node_id(),
357357
node_features: dest.node_features(),
358358
short_channel_id: dest_chan_id,
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, &Some(payment_secret), PaymentId(payment_id)) {
365365
check_payment_err(err);
@@ -374,7 +374,7 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des
374374
payment_id[0..8].copy_from_slice(&payment_idx.to_ne_bytes());
375375
*payment_idx += 1;
376376
if let Err(err) = source.send_payment(&Route {
377-
paths: vec![vec![RouteHop {
377+
paths: vec![Path { hops: vec![RouteHop {
378378
pubkey: middle.get_our_node_id(),
379379
node_features: middle.node_features(),
380380
short_channel_id: middle_chan_id,
@@ -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, &Some(payment_secret), PaymentId(payment_id)) {
394394
check_payment_err(err);

lightning-invoice/src/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1173,7 +1173,7 @@ mod test {
11731173
nodes[fwd_idx].node.process_pending_htlc_forwards();
11741174

11751175
let payment_preimage_opt = if user_generated_pmt_hash { None } else { Some(payment_preimage) };
1176-
expect_payment_claimable!(&nodes[fwd_idx], payment_hash, payment_secret, payment_amt, payment_preimage_opt, route.paths[0].last().unwrap().pubkey);
1176+
expect_payment_claimable!(&nodes[fwd_idx], payment_hash, payment_secret, payment_amt, payment_preimage_opt, route.paths[0].hops.last().unwrap().pubkey);
11771177
do_claim_payment_along_route(&nodes[0], &[&vec!(&nodes[fwd_idx])[..]], false, payment_preimage);
11781178
let events = nodes[0].node.get_and_clear_pending_events();
11791179
assert_eq!(events.len(), 2);

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1974,12 +1974,12 @@ fn test_path_paused_mpp() {
19741974
// Set us up to take multiple routes, one 0 -> 1 -> 3 and one 0 -> 2 -> 3:
19751975
let path = route.paths[0].clone();
19761976
route.paths.push(path);
1977-
route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
1978-
route.paths[0][0].short_channel_id = chan_1_id;
1979-
route.paths[0][1].short_channel_id = chan_3_id;
1980-
route.paths[1][0].pubkey = nodes[2].node.get_our_node_id();
1981-
route.paths[1][0].short_channel_id = chan_2_ann.contents.short_channel_id;
1982-
route.paths[1][1].short_channel_id = chan_4_id;
1977+
route.paths[0].hops[0].pubkey = nodes[1].node.get_our_node_id();
1978+
route.paths[0].hops[0].short_channel_id = chan_1_id;
1979+
route.paths[0].hops[1].short_channel_id = chan_3_id;
1980+
route.paths[1].hops[0].pubkey = nodes[2].node.get_our_node_id();
1981+
route.paths[1].hops[0].short_channel_id = chan_2_ann.contents.short_channel_id;
1982+
route.paths[1].hops[1].short_channel_id = chan_4_id;
19831983

19841984
// Set it so that the first monitor update (for the path 0 -> 1 -> 3) succeeds, but the second
19851985
// (for the path 0 -> 2 -> 3) fails.

lightning/src/ln/channel.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6989,6 +6989,7 @@ mod tests {
69896989
use crate::chain::chaininterface::{FeeEstimator, LowerBoundedFeeEstimator, ConfirmationTarget};
69906990
use crate::chain::keysinterface::{ChannelSigner, InMemorySigner, EntropySource, SignerProvider};
69916991
use crate::chain::transaction::OutPoint;
6992+
use crate::routing::router::Path;
69926993
use crate::util::config::UserConfig;
69936994
use crate::util::enforcing_trait_impls::EnforcingSigner;
69946995
use crate::util::errors::APIError;
@@ -7166,7 +7167,7 @@ mod tests {
71667167
cltv_expiry: 200000000,
71677168
state: OutboundHTLCState::Committed,
71687169
source: HTLCSource::OutboundRoute {
7169-
path: Vec::new(),
7170+
path: Path { hops: Vec::new(), blinded_tail: None },
71707171
session_priv: SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
71717172
first_hop_htlc_msat: 548,
71727173
payment_id: PaymentId([42; 32]),

lightning/src/ln/channelmanager.rs

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, No
4545
#[cfg(any(feature = "_test_utils", test))]
4646
use crate::ln::features::InvoiceFeatures;
4747
use crate::routing::gossip::NetworkGraph;
48-
use crate::routing::router::{DefaultRouter, InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters, RoutePath, Router};
48+
use crate::routing::router::{BlindedTail, DefaultRouter, InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters, Router};
4949
use crate::routing::scoring::ProbabilisticScorer;
5050
use crate::ln::msgs;
5151
use crate::ln::onion_utils;
@@ -280,7 +280,7 @@ impl_writeable_tlv_based_enum!(SentHTLCId,
280280
pub(crate) enum HTLCSource {
281281
PreviousHopData(HTLCPreviousHopData),
282282
OutboundRoute {
283-
path: Vec<RouteHop>,
283+
path: Path,
284284
session_priv: SecretKey,
285285
/// Technically we can recalculate this from the route, but we cache it here to avoid
286286
/// doing a double-pass on route when we get a failure back
@@ -313,7 +313,7 @@ impl core::hash::Hash for HTLCSource {
313313
impl HTLCSource {
314314
pub fn dummy() -> Self {
315315
HTLCSource::OutboundRoute {
316-
path: Vec::new(),
316+
path: Path { hops: Vec::new(), blinded_tail: None },
317317
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
318318
first_hop_htlc_msat: 0,
319319
payment_id: PaymentId([2; 32]),
@@ -2513,16 +2513,16 @@ where
25132513
}
25142514

25152515
#[cfg(test)]
2516-
pub(crate) fn test_send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
2516+
pub(crate) fn test_send_payment_along_path(&self, path: &Path, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
25172517
let _lck = self.total_consistency_lock.read().unwrap();
25182518
self.send_payment_along_path(path, payment_hash, payment_secret, total_value, cur_height, payment_id, keysend_preimage, session_priv_bytes)
25192519
}
25202520

2521-
fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
2521+
fn send_payment_along_path(&self, path: &Path, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
25222522
// The top-level caller should hold the total_consistency_lock read lock.
25232523
debug_assert!(self.total_consistency_lock.try_write().is_err());
25242524

2525-
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
2525+
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first_hop_scid());
25262526
let prng_seed = self.entropy_source.get_secure_random_bytes();
25272527
let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted");
25282528

@@ -2535,7 +2535,7 @@ where
25352535
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
25362536

25372537
let err: Result<(), _> = loop {
2538-
let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.first().unwrap().short_channel_id) {
2538+
let (counterparty_node_id, id) = match self.short_to_chan_info.read().unwrap().get(&path.first_hop_scid()) {
25392539
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()}),
25402540
Some((cp_id, chan_id)) => (cp_id.clone(), chan_id.clone()),
25412541
};
@@ -2587,7 +2587,7 @@ where
25872587
return Ok(());
25882588
};
25892589

2590-
match handle_error!(self, err, path.first().unwrap().pubkey) {
2590+
match handle_error!(self, err, path.first_hop_node_id()) {
25912591
Ok(_) => unreachable!(),
25922592
Err(e) => {
25932593
Err(APIError::ChannelUnavailable { err: e.err })
@@ -5698,7 +5698,7 @@ where
56985698
for chan in peer_state.channel_by_id.values() {
56995699
for (htlc_source, _) in chan.inflight_htlc_sources() {
57005700
if let HTLCSource::OutboundRoute { path, .. } = htlc_source {
5701-
inflight_htlcs.process_path(path, self.get_our_node_id());
5701+
inflight_htlcs.process_path(&path.hops, self.get_our_node_id()); // fixed in upcoming commit
57025702
}
57035703
}
57045704
}
@@ -6902,23 +6902,25 @@ impl Readable for HTLCSource {
69026902
let mut payment_id = None;
69036903
let mut payment_secret = None;
69046904
let mut payment_params: Option<PaymentParameters> = None;
6905+
let mut blinded_tail: Option<BlindedTail> = None;
69056906
read_tlv_fields!(reader, {
69066907
(0, session_priv, required),
69076908
(1, payment_id, option),
69086909
(2, first_hop_htlc_msat, required),
69096910
(3, payment_secret, option),
69106911
(4, path, vec_type),
69116912
(5, payment_params, (option: ReadableArgs, 0)),
6913+
(6, blinded_tail, option),
69126914
});
69136915
if payment_id.is_none() {
69146916
// For backwards compat, if there was no payment_id written, use the session_priv bytes
69156917
// instead.
69166918
payment_id = Some(PaymentId(*session_priv.0.unwrap().as_ref()));
69176919
}
6918-
if path.is_none() || path.as_ref().unwrap().is_empty() {
6920+
let path = path.unwrap_or(Vec::new());
6921+
if path.is_empty() && blinded_tail.is_none() {
69196922
return Err(DecodeError::InvalidValue);
69206923
}
6921-
let path = path.unwrap();
69226924
if let Some(params) = payment_params.as_mut() {
69236925
if params.final_cltv_expiry_delta == 0 {
69246926
params.final_cltv_expiry_delta = path.last().unwrap().cltv_expiry_delta;
@@ -6927,7 +6929,7 @@ impl Readable for HTLCSource {
69276929
Ok(HTLCSource::OutboundRoute {
69286930
session_priv: session_priv.0.unwrap(),
69296931
first_hop_htlc_msat,
6930-
path,
6932+
path: Path { hops: path, blinded_tail },
69316933
payment_id: payment_id.unwrap(),
69326934
payment_secret,
69336935
})
@@ -6949,8 +6951,9 @@ impl Writeable for HTLCSource {
69496951
(1, payment_id_opt, option),
69506952
(2, first_hop_htlc_msat, required),
69516953
(3, payment_secret, option),
6952-
(4, *path, vec_type),
6954+
(4, path.hops, vec_type),
69536955
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
6956+
(6, path.blinded_tail, option),
69546957
});
69556958
}
69566959
HTLCSource::PreviousHopData(ref field) => {
@@ -7612,12 +7615,12 @@ where
76127615
if id_to_peer.get(&monitor.get_funding_txo().0.to_channel_id()).is_none() {
76137616
for (htlc_source, (htlc, _)) in monitor.get_pending_or_resolved_outbound_htlcs() {
76147617
if let HTLCSource::OutboundRoute { payment_id, session_priv, path, payment_secret, .. } = htlc_source {
7615-
if path.is_empty() {
7618+
if path.len() == 0 {
76167619
log_error!(args.logger, "Got an empty path for a pending payment");
76177620
return Err(DecodeError::InvalidValue);
76187621
}
76197622

7620-
let path_amt = path.last().unwrap().fee_msat;
7623+
let path_amt = path.final_value_msat();
76217624
let mut session_priv_bytes = [0; 32];
76227625
session_priv_bytes[..].copy_from_slice(&session_priv[..]);
76237626
match pending_outbounds.pending_outbound_payments.lock().unwrap().entry(payment_id) {
@@ -7627,7 +7630,7 @@ where
76277630
if newly_added { "Added" } else { "Had" }, path_amt, log_bytes!(session_priv_bytes), log_bytes!(htlc.payment_hash.0));
76287631
},
76297632
hash_map::Entry::Vacant(entry) => {
7630-
let path_fee = path.get_path_fees();
7633+
let path_fee = path.fee_msat();
76317634
entry.insert(PendingOutboundPayment::Retryable {
76327635
retry_strategy: None,
76337636
attempts: PaymentAttempts::new(),
@@ -8130,15 +8133,15 @@ mod tests {
81308133
Event::PaymentPathSuccessful { payment_id: ref actual_payment_id, ref payment_hash, ref path } => {
81318134
assert_eq!(payment_id, *actual_payment_id);
81328135
assert_eq!(our_payment_hash, *payment_hash.as_ref().unwrap());
8133-
assert_eq!(route.paths[0], *path);
8136+
assert_eq!(route.paths[0].hops, *path);
81348137
},
81358138
_ => panic!("Unexpected event"),
81368139
}
81378140
match events[2] {
81388141
Event::PaymentPathSuccessful { payment_id: ref actual_payment_id, ref payment_hash, ref path } => {
81398142
assert_eq!(payment_id, *actual_payment_id);
81408143
assert_eq!(our_payment_hash, *payment_hash.as_ref().unwrap());
8141-
assert_eq!(route.paths[0], *path);
8144+
assert_eq!(route.paths[0].hops, *path);
81428145
},
81438146
_ => panic!("Unexpected event"),
81448147
}
@@ -8342,12 +8345,12 @@ mod tests {
83428345
let (mut route, payment_hash, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[3], 100000);
83438346
let path = route.paths[0].clone();
83448347
route.paths.push(path);
8345-
route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
8346-
route.paths[0][0].short_channel_id = chan_1_id;
8347-
route.paths[0][1].short_channel_id = chan_3_id;
8348-
route.paths[1][0].pubkey = nodes[2].node.get_our_node_id();
8349-
route.paths[1][0].short_channel_id = chan_2_id;
8350-
route.paths[1][1].short_channel_id = chan_4_id;
8348+
route.paths[0].hops[0].pubkey = nodes[1].node.get_our_node_id();
8349+
route.paths[0].hops[0].short_channel_id = chan_1_id;
8350+
route.paths[0].hops[1].short_channel_id = chan_3_id;
8351+
route.paths[1].hops[0].pubkey = nodes[2].node.get_our_node_id();
8352+
route.paths[1].hops[0].short_channel_id = chan_2_id;
8353+
route.paths[1].hops[1].short_channel_id = chan_4_id;
83518354

83528355
match nodes[0].node.send_payment(&route, payment_hash, &None, PaymentId(payment_hash.0)).unwrap_err() {
83538356
PaymentSendFailure::ParameterError(APIError::APIMisuseError { ref err }) => {

lightning/src/ln/functional_test_utils.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,7 +2189,7 @@ pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route:
21892189
let route = get_route(origin_node, &payment_params, recv_value, TEST_FINAL_CLTV).unwrap();
21902190
assert_eq!(route.paths.len(), 1);
21912191
assert_eq!(route.paths[0].len(), expected_route.len());
2192-
for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
2192+
for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) {
21932193
assert_eq!(hop.pubkey, node.node.get_our_node_id());
21942194
}
21952195

@@ -2210,7 +2210,7 @@ pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_rou
22102210
None, recv_value, TEST_FINAL_CLTV, origin_node.logger, &scorer, &random_seed_bytes).unwrap();
22112211
assert_eq!(route.paths.len(), 1);
22122212
assert_eq!(route.paths[0].len(), expected_route.len());
2213-
for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
2213+
for (node, hop) in expected_route.iter().zip(route.paths[0].hops.iter()) {
22142214
assert_eq!(hop.pubkey, node.node.get_our_node_id());
22152215
}
22162216

0 commit comments

Comments
 (0)