Skip to content

Commit ba9e674

Browse files
committed
Expand the Route object to include multiple paths.
Rather big diff, but its all mechanical and doesn't introduce any new features.
1 parent 5ec5470 commit ba9e674

File tree

7 files changed

+334
-292
lines changed

7 files changed

+334
-292
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -409,14 +409,14 @@ pub fn do_test(data: &[u8]) {
409409
let payment_hash = Sha256::hash(&[payment_id; 1]);
410410
payment_id = payment_id.wrapping_add(1);
411411
if let Err(_) = $source.send_payment(Route {
412-
hops: vec![RouteHop {
412+
paths: vec![vec![RouteHop {
413413
pubkey: $dest.0.get_our_node_id(),
414414
node_features: NodeFeatures::empty(),
415415
short_channel_id: $dest.1,
416416
channel_features: ChannelFeatures::empty(),
417417
fee_msat: 5000000,
418418
cltv_expiry_delta: 200,
419-
}],
419+
}]],
420420
}, PaymentHash(payment_hash.into_inner()), &None) {
421421
// Probably ran out of funds
422422
test_return!();
@@ -426,7 +426,7 @@ pub fn do_test(data: &[u8]) {
426426
let payment_hash = Sha256::hash(&[payment_id; 1]);
427427
payment_id = payment_id.wrapping_add(1);
428428
if let Err(_) = $source.send_payment(Route {
429-
hops: vec![RouteHop {
429+
paths: vec![vec![RouteHop {
430430
pubkey: $middle.0.get_our_node_id(),
431431
node_features: NodeFeatures::empty(),
432432
short_channel_id: $middle.1,
@@ -440,7 +440,7 @@ pub fn do_test(data: &[u8]) {
440440
channel_features: ChannelFeatures::empty(),
441441
fee_msat: 5000000,
442442
cltv_expiry_delta: 200,
443-
}],
443+
}]],
444444
}, PaymentHash(payment_hash.into_inner()), &None) {
445445
// Probably ran out of funds
446446
test_return!();

lightning/src/ln/channelmanager.rs

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use chain::transaction::OutPoint;
3030
use ln::channel::{Channel, ChannelError};
3131
use ln::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, ManyChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
3232
use ln::features::{InitFeatures, NodeFeatures};
33-
use ln::router::Route;
33+
use ln::router::{Route, RouteHop};
3434
use ln::msgs;
3535
use ln::onion_utils;
3636
use ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
@@ -136,7 +136,7 @@ struct ClaimableHTLC {
136136
pub(super) enum HTLCSource {
137137
PreviousHopData(HTLCPreviousHopData),
138138
OutboundRoute {
139-
route: Route,
139+
path: Vec<RouteHop>,
140140
session_priv: SecretKey,
141141
/// Technically we can recalculate this from the route, but we cache it here to avoid
142142
/// doing a double-pass on route when we get a failure back
@@ -147,7 +147,7 @@ pub(super) enum HTLCSource {
147147
impl HTLCSource {
148148
pub fn dummy() -> Self {
149149
HTLCSource::OutboundRoute {
150-
route: Route { hops: Vec::new() },
150+
path: Vec::new(),
151151
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
152152
first_hop_htlc_msat: 0,
153153
}
@@ -1231,23 +1231,26 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
12311231
/// bit set (either as required or as available). If multiple paths are present in the Route,
12321232
/// we assume the invoice had the basic_mpp feature set.
12331233
pub fn send_payment(&self, route: Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>) -> Result<(), APIError> {
1234-
if route.hops.len() < 1 || route.hops.len() > 20 {
1235-
return Err(APIError::RouteError{err: "Route didn't go anywhere/had bogus size"});
1234+
if route.paths.len() < 1 || route.paths.len() > 1 {
1235+
return Err(APIError::RouteError{err: "We currently don't support MPP, and we need at least one path"});
1236+
}
1237+
if route.paths[0].len() < 1 || route.paths[0].len() > 20 {
1238+
return Err(APIError::RouteError{err: "Path didn't go anywhere/had bogus size"});
12361239
}
12371240
let our_node_id = self.get_our_node_id();
1238-
for (idx, hop) in route.hops.iter().enumerate() {
1239-
if idx != route.hops.len() - 1 && hop.pubkey == our_node_id {
1240-
return Err(APIError::RouteError{err: "Route went through us but wasn't a simple rebalance loop to us"});
1241+
for (idx, hop) in route.paths[0].iter().enumerate() {
1242+
if idx != route.paths[0].len() - 1 && hop.pubkey == our_node_id {
1243+
return Err(APIError::RouteError{err: "Path went through us but wasn't a simple rebalance loop to us"});
12411244
}
12421245
}
12431246

12441247
let (session_priv, prng_seed) = self.keys_manager.get_onion_rand();
12451248

12461249
let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1;
12471250

1248-
let onion_keys = secp_call!(onion_utils::construct_onion_keys(&self.secp_ctx, &route, &session_priv),
1251+
let onion_keys = secp_call!(onion_utils::construct_onion_keys(&self.secp_ctx, &route.paths[0], &session_priv),
12491252
APIError::RouteError{err: "Pubkey along hop was maliciously selected"});
1250-
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route, payment_secret, cur_height)?;
1253+
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], payment_secret, cur_height)?;
12511254
if onion_utils::route_size_insane(&onion_payloads) {
12521255
return Err(APIError::RouteError{err: "Route size too large considering onion data"});
12531256
}
@@ -1257,22 +1260,22 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
12571260

12581261
let err: Result<(), _> = loop {
12591262
let mut channel_lock = self.channel_state.lock().unwrap();
1260-
let id = match channel_lock.short_to_id.get(&route.hops.first().unwrap().short_channel_id) {
1263+
let id = match channel_lock.short_to_id.get(&route.paths[0].first().unwrap().short_channel_id) {
12611264
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!"}),
12621265
Some(id) => id.clone(),
12631266
};
12641267

12651268
let channel_state = &mut *channel_lock;
12661269
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
12671270
match {
1268-
if chan.get().get_their_node_id() != route.hops.first().unwrap().pubkey {
1271+
if chan.get().get_their_node_id() != route.paths[0].first().unwrap().pubkey {
12691272
return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"});
12701273
}
12711274
if !chan.get().is_live() {
12721275
return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!"});
12731276
}
12741277
break_chan_entry!(self, chan.get_mut().send_htlc_and_commit(htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute {
1275-
route: route.clone(),
1278+
path: route.paths[0].clone(),
12761279
session_priv: session_priv.clone(),
12771280
first_hop_htlc_msat: htlc_msat,
12781281
}, onion_packet), channel_state, chan)
@@ -1288,7 +1291,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
12881291
}
12891292

12901293
channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
1291-
node_id: route.hops.first().unwrap().pubkey,
1294+
node_id: route.paths[0].first().unwrap().pubkey,
12921295
updates: msgs::CommitmentUpdate {
12931296
update_add_htlcs: vec![update_add],
12941297
update_fulfill_htlcs: Vec::new(),
@@ -1305,7 +1308,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
13051308
return Ok(());
13061309
};
13071310

1308-
match handle_error!(self, err, route.hops.first().unwrap().pubkey) {
1311+
match handle_error!(self, err, route.paths[0].first().unwrap().pubkey) {
13091312
Ok(_) => unreachable!(),
13101313
Err(e) => { Err(APIError::ChannelUnavailable { err: e.err }) }
13111314
}
@@ -1750,7 +1753,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
17501753
//between the branches here. We should make this async and move it into the forward HTLCs
17511754
//timer handling.
17521755
match source {
1753-
HTLCSource::OutboundRoute { ref route, .. } => {
1756+
HTLCSource::OutboundRoute { ref path, .. } => {
17541757
log_trace!(self, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
17551758
mem::drop(channel_state_lock);
17561759
match &onion_error {
@@ -1792,7 +1795,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
17921795
self.pending_events.lock().unwrap().push(
17931796
events::Event::PaymentFailed {
17941797
payment_hash: payment_hash.clone(),
1795-
rejected_by_dest: route.hops.len() == 1,
1798+
rejected_by_dest: path.len() == 1,
17961799
#[cfg(test)]
17971800
error_code: Some(*failure_code),
17981801
}
@@ -1856,9 +1859,19 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
18561859
let mut channel_state = Some(self.channel_state.lock().unwrap());
18571860
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&(payment_hash, *payment_secret));
18581861
if let Some(mut sources) = removed_source {
1862+
assert!(!sources.is_empty());
1863+
let valid_mpp_amount = if let &Some(ref data) = &sources[0].payment_data {
1864+
assert!(payment_secret.is_some());
1865+
data.total_msat == expected_amount
1866+
} else {
1867+
assert!(payment_secret.is_none());
1868+
false
1869+
};
1870+
1871+
let mut claimed_any_htlcs = false;
18591872
for htlc in sources.drain(..) {
18601873
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
1861-
if htlc.value < expected_amount || htlc.value > expected_amount * 2 {
1874+
if !valid_mpp_amount && (htlc.value < expected_amount || htlc.value > expected_amount * 2) {
18621875
let mut htlc_msat_data = byte_utils::be64_to_array(htlc.value).to_vec();
18631876
let mut height_data = byte_utils::be32_to_array(self.latest_block_height.load(Ordering::Acquire) as u32).to_vec();
18641877
htlc_msat_data.append(&mut height_data);
@@ -1867,9 +1880,10 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
18671880
HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_data });
18681881
} else {
18691882
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc.prev_hop), payment_preimage);
1883+
claimed_any_htlcs = true;
18701884
}
18711885
}
1872-
true
1886+
claimed_any_htlcs
18731887
} else { false }
18741888
}
18751889
fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard<ChannelHolder<ChanSigner>>, source: HTLCSource, payment_preimage: PaymentPreimage) {
@@ -3271,9 +3285,9 @@ impl Writeable for HTLCSource {
32713285
0u8.write(writer)?;
32723286
hop_data.write(writer)?;
32733287
},
3274-
&HTLCSource::OutboundRoute { ref route, ref session_priv, ref first_hop_htlc_msat } => {
3288+
&HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat } => {
32753289
1u8.write(writer)?;
3276-
route.write(writer)?;
3290+
path.write(writer)?;
32773291
session_priv.write(writer)?;
32783292
first_hop_htlc_msat.write(writer)?;
32793293
}
@@ -3287,7 +3301,7 @@ impl Readable for HTLCSource {
32873301
match <u8 as Readable>::read(reader)? {
32883302
0 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
32893303
1 => Ok(HTLCSource::OutboundRoute {
3290-
route: Readable::read(reader)?,
3304+
path: Readable::read(reader)?,
32913305
session_priv: Readable::read(reader)?,
32923306
first_hop_htlc_msat: Readable::read(reader)?,
32933307
}),

lightning/src/ln/functional_test_utils.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -897,8 +897,9 @@ pub const TEST_FINAL_CLTV: u32 = 32;
897897

898898
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash) {
899899
let route = origin_node.router.get_route(&expected_route.last().unwrap().node.get_our_node_id(), None, &Vec::new(), recv_value, TEST_FINAL_CLTV).unwrap();
900-
assert_eq!(route.hops.len(), expected_route.len());
901-
for (node, hop) in expected_route.iter().zip(route.hops.iter()) {
900+
assert_eq!(route.paths.len(), 1);
901+
assert_eq!(route.paths[0].len(), expected_route.len());
902+
for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
902903
assert_eq!(hop.pubkey, node.node.get_our_node_id());
903904
}
904905

@@ -907,8 +908,9 @@ pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route:
907908

908909
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
909910
let route = origin_node.router.get_route(&expected_route.last().unwrap().node.get_our_node_id(), None, &Vec::new(), recv_value, TEST_FINAL_CLTV).unwrap();
910-
assert_eq!(route.hops.len(), expected_route.len());
911-
for (node, hop) in expected_route.iter().zip(route.hops.iter()) {
911+
assert_eq!(route.paths.len(), 1);
912+
assert_eq!(route.paths[0].len(), expected_route.len());
913+
for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
912914
assert_eq!(hop.pubkey, node.node.get_our_node_id());
913915
}
914916

0 commit comments

Comments
 (0)