Skip to content

Commit 1b8bf5e

Browse files
committed
Multi-path payments if can't find a sufficient path
1 parent 134add8 commit 1b8bf5e

File tree

1 file changed

+76
-41
lines changed

1 file changed

+76
-41
lines changed

lightning/src/routing/router.rs

Lines changed: 76 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use util::ser::{Writeable, Readable};
1313
use util::logger::Logger;
1414

1515
use std::cmp;
16-
use std::collections::{HashMap,BinaryHeap};
16+
use std::collections::{HashMap, BinaryHeap, HashSet};
1717
use std::ops::Deref;
1818

1919
/// A hop in a route
@@ -119,7 +119,7 @@ pub struct RouteHint {
119119
pub htlc_maximum_msat: Option<u64>,
120120
}
121121

122-
#[derive(Eq, PartialEq)]
122+
#[derive(Eq, PartialEq, Clone)]
123123
struct RouteGraphNode {
124124
pubkey: PublicKey,
125125
lowest_fee_to_peer_through_node: u64,
@@ -336,7 +336,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, targ
336336
}
337337

338338
macro_rules! add_entries_to_cheapest_to_target_node {
339-
( $node: expr, $node_id: expr, $fee_to_target_msat: expr ) => {
339+
( $node: expr, $node_id: expr, $fee_to_target_msat: expr, $channels_to_avoid: expr ) => {
340340
if first_hops.is_some() {
341341
if let Some(&(ref first_hop, ref features)) = first_hop_targets.get(&$node_id) {
342342
add_entry!(first_hop, *our_node_id, $node_id, dummy_directional_info, None::<u64>, features.to_context(), $fee_to_target_msat);
@@ -352,6 +352,9 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, targ
352352

353353
if !features.requires_unknown_bits() {
354354
for chan_id in $node.channels.iter() {
355+
if $channels_to_avoid.contains(chan_id) {
356+
continue;
357+
}
355358
let chan = network.get_channels().get(chan_id).unwrap();
356359
if !chan.features.requires_unknown_bits() {
357360
if chan.node_one == *$node_id {
@@ -382,7 +385,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, targ
382385
match network.get_nodes().get(target) {
383386
None => {},
384387
Some(node) => {
385-
add_entries_to_cheapest_to_target_node!(node, target, 0);
388+
add_entries_to_cheapest_to_target_node!(node, target, 0, HashSet::<u64>::new());
386389
},
387390
}
388391

@@ -405,53 +408,85 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, targ
405408
}
406409
}
407410

408-
while let Some(RouteGraphNode { pubkey, lowest_fee_to_node, .. }) = targets.pop() {
409-
if pubkey == *our_node_id {
410-
let mut res = vec!(dist.remove(&our_node_id).unwrap().3);
411-
loop {
412-
if let Some(&(_, ref features)) = first_hop_targets.get(&res.last().unwrap().pubkey) {
413-
res.last_mut().unwrap().node_features = features.to_context();
414-
} else if let Some(node) = network.get_nodes().get(&res.last().unwrap().pubkey) {
415-
if let Some(node_info) = node.announcement_info.as_ref() {
416-
res.last_mut().unwrap().node_features = node_info.features.clone();
411+
let mut payment_paths = Vec::new();
412+
let mut channels_to_avoid = HashSet::new();
413+
let mut paths_cap = 3; // TODO: be smarter at limiting paths number considering the known available capacity.
414+
'route_finding: loop {
415+
let mut cur_dist = dist.clone();
416+
let mut cur_targets = targets.clone();
417+
let mut using_low_capacity_channel = false;
418+
while let Some(RouteGraphNode { pubkey, lowest_fee_to_node, .. }) = cur_targets.pop() {
419+
if pubkey == *our_node_id {
420+
let mut new_entry = cur_dist.remove(&our_node_id).unwrap();
421+
let mut res = vec!(new_entry.3.clone());
422+
loop {
423+
if let Some(&(_, ref features)) = first_hop_targets.get(&res.last().unwrap().pubkey) {
424+
res.last_mut().unwrap().node_features = features.to_context();
425+
} else if let Some(node) = network.get_nodes().get(&res.last().unwrap().pubkey) {
426+
if let Some(node_info) = node.announcement_info.as_ref() {
427+
res.last_mut().unwrap().node_features = node_info.features.clone();
428+
} else {
429+
res.last_mut().unwrap().node_features = NodeFeatures::empty();
430+
}
417431
} else {
418-
res.last_mut().unwrap().node_features = NodeFeatures::empty();
432+
// We should be able to fill in features for everything except the last
433+
// hop, if the last hop was provided via a BOLT 11 invoice (though we
434+
// should be able to extend it further as BOLT 11 does have feature
435+
// flags for the last hop node itself).
436+
assert!(res.last().unwrap().pubkey == *target);
419437
}
420-
} else {
421-
// We should be able to fill in features for everything except the last
422-
// hop, if the last hop was provided via a BOLT 11 invoice (though we
423-
// should be able to extend it further as BOLT 11 does have feature
424-
// flags for the last hop node itself).
425-
assert!(res.last().unwrap().pubkey == *target);
438+
439+
if let Some(available_msat) = new_entry.4 {
440+
if available_msat < recommended_available_msat {
441+
using_low_capacity_channel = true;
442+
}
443+
} else {
444+
using_low_capacity_channel = true;
445+
}
446+
447+
if using_low_capacity_channel {
448+
// Do not use low-capacity channel for next MPP try.
449+
channels_to_avoid.insert(new_entry.3.short_channel_id);
450+
}
451+
452+
// TODO: for next MPP path, reduce the available capacity of channels
453+
// used in the MPP path of current iteration.
454+
455+
if res.last().unwrap().pubkey == *target {
456+
break;
457+
}
458+
459+
new_entry = match cur_dist.remove(&res.last().unwrap().pubkey) {
460+
Some(hop) => hop,
461+
None => return Err(LightningError{err: "Failed to find a non-fee-overflowing path to the given destination".to_owned(), action: ErrorAction::IgnoreError}),
462+
};
463+
res.last_mut().unwrap().fee_msat = new_entry.3.fee_msat;
464+
res.last_mut().unwrap().cltv_expiry_delta = new_entry.3.cltv_expiry_delta;
465+
res.push(new_entry.3.clone());
426466
}
427-
if res.last().unwrap().pubkey == *target {
428-
break;
467+
res.last_mut().unwrap().fee_msat = final_value_msat;
468+
res.last_mut().unwrap().cltv_expiry_delta = final_cltv;
469+
payment_paths.push(res);
470+
tries += 1;
471+
if !using_low_capacity_channel || tries == 5 {
472+
break 'route_finding;
429473
}
474+
}
430475

431-
let new_entry = match dist.remove(&res.last().unwrap().pubkey) {
432-
Some(hop) => hop.3,
433-
None => return Err(LightningError{err: "Failed to find a non-fee-overflowing path to the given destination".to_owned(), action: ErrorAction::IgnoreError}),
434-
};
435-
res.last_mut().unwrap().fee_msat = new_entry.fee_msat;
436-
res.last_mut().unwrap().cltv_expiry_delta = new_entry.cltv_expiry_delta;
437-
res.push(new_entry);
476+
match network.get_nodes().get(&pubkey) {
477+
None => {},
478+
Some(node) => {
479+
add_entries_to_cheapest_to_target_node!(node, &pubkey, lowest_fee_to_node, channels_to_avoid);
480+
},
438481
}
439-
res.last_mut().unwrap().fee_msat = final_value_msat;
440-
res.last_mut().unwrap().cltv_expiry_delta = final_cltv;
441-
let route = Route { paths: vec![res] };
442-
log_trace!(logger, "Got route: {}", log_route!(route));
443-
return Ok(route);
444482
}
445483

446-
match network.get_nodes().get(&pubkey) {
447-
None => {},
448-
Some(node) => {
449-
add_entries_to_cheapest_to_target_node!(node, &pubkey, lowest_fee_to_node);
450-
},
451-
}
484+
return Err(LightningError{err: "Failed to find a path to the given destination".to_owned(), action: ErrorAction::IgnoreError});
452485
}
453486

454-
Err(LightningError{err: "Failed to find a path to the given destination".to_owned(), action: ErrorAction::IgnoreError})
487+
let route = Route { paths: payment_paths };
488+
log_trace!(logger, "Got route: {}", log_route!(route));
489+
return Ok(route);
455490
}
456491

457492
#[cfg(test)]

0 commit comments

Comments
 (0)