Skip to content

Commit a563a8b

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 26c4746 commit a563a8b

File tree

7 files changed

+346
-304
lines changed

7 files changed

+346
-304
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
}
@@ -1230,23 +1230,26 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
12301230
/// bit set (either as required or as available). If multiple paths are present in the Route,
12311231
/// we assume the invoice had the basic_mpp feature set.
12321232
pub fn send_payment(&self, route: Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>) -> Result<(), APIError> {
1233-
if route.hops.len() < 1 || route.hops.len() > 20 {
1234-
return Err(APIError::RouteError{err: "Route didn't go anywhere/had bogus size"});
1233+
if route.paths.len() < 1 || route.paths.len() > 1 {
1234+
return Err(APIError::RouteError{err: "We currently don't support MPP, and we need at least one path"});
1235+
}
1236+
if route.paths[0].len() < 1 || route.paths[0].len() > 20 {
1237+
return Err(APIError::RouteError{err: "Path didn't go anywhere/had bogus size"});
12351238
}
12361239
let our_node_id = self.get_our_node_id();
1237-
for (idx, hop) in route.hops.iter().enumerate() {
1238-
if idx != route.hops.len() - 1 && hop.pubkey == our_node_id {
1239-
return Err(APIError::RouteError{err: "Route went through us but wasn't a simple rebalance loop to us"});
1240+
for (idx, hop) in route.paths[0].iter().enumerate() {
1241+
if idx != route.paths[0].len() - 1 && hop.pubkey == our_node_id {
1242+
return Err(APIError::RouteError{err: "Path went through us but wasn't a simple rebalance loop to us"});
12401243
}
12411244
}
12421245

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

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

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

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

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

12891292
channel_state.pending_msg_events.push(events::MessageSendEvent::UpdateHTLCs {
1290-
node_id: route.hops.first().unwrap().pubkey,
1293+
node_id: route.paths[0].first().unwrap().pubkey,
12911294
updates: msgs::CommitmentUpdate {
12921295
update_add_htlcs: vec![update_add],
12931296
update_fulfill_htlcs: Vec::new(),
@@ -1304,7 +1307,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
13041307
return Ok(());
13051308
};
13061309

1307-
match handle_error!(self, err, route.hops.first().unwrap().pubkey) {
1310+
match handle_error!(self, err, route.paths[0].first().unwrap().pubkey) {
13081311
Ok(_) => unreachable!(),
13091312
Err(e) => { Err(APIError::ChannelUnavailable { err: e.err }) }
13101313
}
@@ -1749,7 +1752,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
17491752
//between the branches here. We should make this async and move it into the forward HTLCs
17501753
//timer handling.
17511754
match source {
1752-
HTLCSource::OutboundRoute { ref route, .. } => {
1755+
HTLCSource::OutboundRoute { ref path, .. } => {
17531756
log_trace!(self, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
17541757
mem::drop(channel_state_lock);
17551758
match &onion_error {
@@ -1791,7 +1794,7 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
17911794
self.pending_events.lock().unwrap().push(
17921795
events::Event::PaymentFailed {
17931796
payment_hash: payment_hash.clone(),
1794-
rejected_by_dest: route.hops.len() == 1,
1797+
rejected_by_dest: path.len() == 1,
17951798
#[cfg(test)]
17961799
error_code: Some(*failure_code),
17971800
}
@@ -1855,9 +1858,19 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
18551858
let mut channel_state = Some(self.channel_state.lock().unwrap());
18561859
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&(payment_hash, *payment_secret));
18571860
if let Some(mut sources) = removed_source {
1861+
assert!(!sources.is_empty());
1862+
let valid_mpp_amount = if let &Some(ref data) = &sources[0].payment_data {
1863+
assert!(payment_secret.is_some());
1864+
data.total_msat == expected_amount
1865+
} else {
1866+
assert!(payment_secret.is_none());
1867+
false
1868+
};
1869+
1870+
let mut claimed_any_htlcs = false;
18581871
for htlc in sources.drain(..) {
18591872
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
1860-
if htlc.value < expected_amount || htlc.value > expected_amount * 2 {
1873+
if !valid_mpp_amount && (htlc.value < expected_amount || htlc.value > expected_amount * 2) {
18611874
let mut htlc_msat_data = byte_utils::be64_to_array(htlc.value).to_vec();
18621875
let mut height_data = byte_utils::be32_to_array(self.latest_block_height.load(Ordering::Acquire) as u32).to_vec();
18631876
htlc_msat_data.append(&mut height_data);
@@ -1866,9 +1879,10 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref> ChannelMan
18661879
HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_data });
18671880
} else {
18681881
self.claim_funds_internal(channel_state.take().unwrap(), HTLCSource::PreviousHopData(htlc.prev_hop), payment_preimage);
1882+
claimed_any_htlcs = true;
18691883
}
18701884
}
1871-
true
1885+
claimed_any_htlcs
18721886
} else { false }
18731887
}
18741888
fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard<ChannelHolder<ChanSigner>>, source: HTLCSource, payment_preimage: PaymentPreimage) {
@@ -3270,9 +3284,9 @@ impl Writeable for HTLCSource {
32703284
0u8.write(writer)?;
32713285
hop_data.write(writer)?;
32723286
},
3273-
&HTLCSource::OutboundRoute { ref route, ref session_priv, ref first_hop_htlc_msat } => {
3287+
&HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat } => {
32743288
1u8.write(writer)?;
3275-
route.write(writer)?;
3289+
path.write(writer)?;
32763290
session_priv.write(writer)?;
32773291
first_hop_htlc_msat.write(writer)?;
32783292
}
@@ -3286,7 +3300,7 @@ impl Readable for HTLCSource {
32863300
match <u8 as Readable>::read(reader)? {
32873301
0 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
32883302
1 => Ok(HTLCSource::OutboundRoute {
3289-
route: Readable::read(reader)?,
3303+
path: Readable::read(reader)?,
32903304
session_priv: Readable::read(reader)?,
32913305
first_hop_htlc_msat: Readable::read(reader)?,
32923306
}),

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)