Skip to content

Commit 99a123b

Browse files
Account for used liquidity in first hops when processing route hints
.. in get_route.
1 parent 811698c commit 99a123b

File tree

1 file changed

+36
-26
lines changed

1 file changed

+36
-26
lines changed

lightning/src/routing/router.rs

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,27 +1442,38 @@ where L::Target: Logger {
14421442
// when we want to stop looking for new paths.
14431443
let mut already_collected_value_msat = 0;
14441444

1445+
macro_rules! sort_first_hop_channels {
1446+
($channels: expr) => {
1447+
// Sort the first_hops channels to the same node(s) in priority order of which channel we'd
1448+
// most like to use.
1449+
//
1450+
// First, if channels are below `recommended_value_msat`, sort them in descending order,
1451+
// preferring larger channels to avoid splitting the payment into more MPP parts than is
1452+
// required.
1453+
//
1454+
// Second, because simply always sorting in descending order would always use our largest
1455+
// available outbound capacity, needlessly fragmenting our available channel capacities,
1456+
// sort channels above `recommended_value_msat` in ascending order, preferring channels
1457+
// which have enough, but not too much, capacity for the payment.
1458+
$channels.sort_unstable_by(|chan_a, chan_b| {
1459+
let chan_a_outbound_limit_msat = chan_a.next_outbound_htlc_limit_msat
1460+
.saturating_sub(*used_channel_liquidities.get(&(chan_a.get_outbound_payment_scid().unwrap(),
1461+
our_node_pubkey < &chan_a.counterparty.node_id)).unwrap_or(&0));
1462+
let chan_b_outbound_limit_msat = chan_b.next_outbound_htlc_limit_msat
1463+
.saturating_sub(*used_channel_liquidities.get(&(chan_b.get_outbound_payment_scid().unwrap(),
1464+
our_node_pubkey < &chan_b.counterparty.node_id)).unwrap_or(&0));
1465+
if chan_b_outbound_limit_msat < recommended_value_msat || chan_a_outbound_limit_msat < recommended_value_msat {
1466+
// Sort in descending order
1467+
chan_b_outbound_limit_msat.cmp(&chan_a_outbound_limit_msat)
1468+
} else {
1469+
// Sort in ascending order
1470+
chan_a_outbound_limit_msat.cmp(&chan_b_outbound_limit_msat)
1471+
}
1472+
});
1473+
}
1474+
}
14451475
for (_, channels) in first_hop_targets.iter_mut() {
1446-
// Sort the first_hops channels to the same node(s) in priority order of which channel we'd
1447-
// most like to use.
1448-
//
1449-
// First, if channels are below `recommended_value_msat`, sort them in descending order,
1450-
// preferring larger channels to avoid splitting the payment into more MPP parts than is
1451-
// required.
1452-
//
1453-
// Second, because simply always sorting in descending order would always use our largest
1454-
// available outbound capacity, needlessly fragmenting our available channel capacities,
1455-
// sort channels above `recommended_value_msat` in ascending order, preferring channels
1456-
// which have enough, but not too much, capacity for the payment.
1457-
channels.sort_unstable_by(|chan_a, chan_b| {
1458-
if chan_b.next_outbound_htlc_limit_msat < recommended_value_msat || chan_a.next_outbound_htlc_limit_msat < recommended_value_msat {
1459-
// Sort in descending order
1460-
chan_b.next_outbound_htlc_limit_msat.cmp(&chan_a.next_outbound_htlc_limit_msat)
1461-
} else {
1462-
// Sort in ascending order
1463-
chan_a.next_outbound_htlc_limit_msat.cmp(&chan_b.next_outbound_htlc_limit_msat)
1464-
}
1465-
});
1476+
sort_first_hop_channels!(channels);
14661477
}
14671478

14681479
log_trace!(logger, "Building path from {} to payer {} for value {} msat.",
@@ -1871,7 +1882,8 @@ where L::Target: Logger {
18711882
.saturating_add(1);
18721883

18731884
// Searching for a direct channel between last checked hop and first_hop_targets
1874-
if let Some(first_channels) = first_hop_targets.get(&NodeId::from_pubkey(&prev_hop_id)) {
1885+
if let Some(first_channels) = first_hop_targets.get_mut(&NodeId::from_pubkey(&prev_hop_id)) {
1886+
sort_first_hop_channels!(first_channels);
18751887
for details in first_channels {
18761888
let first_hop_candidate = CandidateRouteHop::FirstHop { details };
18771889
add_entry!(first_hop_candidate, our_node_id, NodeId::from_pubkey(&prev_hop_id),
@@ -1910,7 +1922,8 @@ where L::Target: Logger {
19101922
// Note that we *must* check if the last hop was added as `add_entry`
19111923
// always assumes that the third argument is a node to which we have a
19121924
// path.
1913-
if let Some(first_channels) = first_hop_targets.get(&NodeId::from_pubkey(&hop.src_node_id)) {
1925+
if let Some(first_channels) = first_hop_targets.get_mut(&NodeId::from_pubkey(&hop.src_node_id)) {
1926+
sort_first_hop_channels!(first_channels);
19141927
for details in first_channels {
19151928
let first_hop_candidate = CandidateRouteHop::FirstHop { details };
19161929
add_entry!(first_hop_candidate, our_node_id,
@@ -6018,12 +6031,9 @@ mod tests {
60186031
let route = get_route(&our_node_id, &payment_params, &network_graph.read_only(),
60196032
Some(&first_hops.iter().collect::<Vec<_>>()), amt_msat, Arc::clone(&logger), &scorer, &(),
60206033
&random_seed_bytes).unwrap();
6021-
// TODO: `get_route` returns a suboptimal route here because first hop channels are not
6022-
// resorted on the fly when processing route hints.
6023-
assert_eq!(route.paths.len(), 3);
6034+
assert_eq!(route.paths.len(), 2);
60246035
assert!(route.paths[0].hops.last().unwrap().fee_msat <= max_htlc_msat);
60256036
assert!(route.paths[1].hops.last().unwrap().fee_msat <= max_htlc_msat);
6026-
assert!(route.paths[2].hops.last().unwrap().fee_msat <= max_htlc_msat);
60276037
assert_eq!(route.get_total_amount(), amt_msat);
60286038
}
60296039

0 commit comments

Comments
 (0)