Skip to content

Commit b1bc078

Browse files
committed
Avoid needless MPP on multiple channels to the same first-hop
When we have many channels to the same first-hop, many of which do not have sufficient balance to make the requested payment, but when some do, instead of simply using the available channel balance we may switch to MPP, potentially with many, many paths. Instead, we should seek to use the smallest channel which can easily handle the requested payment, which we do here by sorting the first_hops in our router before beginning the graph search. Note that the "real" fix for this should be to instead decide which channel to use at HTLC-send time, as most other nodes do during relay, but this provides a minimal fix without needing to do the rather-large work of refactoring our HTLC send+relay pipelines. Issues with overly-aggressive MPP on many channels were reported by Cash App.
1 parent 5ed2985 commit b1bc078

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed

lightning/src/routing/router.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,29 @@ where L::Target: Logger {
811811
// - when we want to stop looking for new paths.
812812
let mut already_collected_value_msat = 0;
813813

814+
for (_, channels) in first_hop_targets.iter_mut() {
815+
// Sort the first_hops channels to the same node(s) in priority order of which channel we'd
816+
// most like to use.
817+
//
818+
// First, if channels are below `recommended_value_msat`, sort them in descending order,
819+
// preferring larger channels to avoid splitting the payment into more MPP parts than is
820+
// required.
821+
//
822+
// Second, because simply always sorting in descending order would always use our largest
823+
// available outbound capacity, needlessly fragmenting our available channel capacities,
824+
// sort channels above `recommended_value_msat` in ascending order, preferring channels
825+
// which have enough, but not too much, capacity for the payment.
826+
channels.sort_unstable_by(|chan_a, chan_b| {
827+
if chan_b.outbound_capacity_msat < recommended_value_msat || chan_a.outbound_capacity_msat < recommended_value_msat {
828+
// Sort in descending order
829+
chan_b.outbound_capacity_msat.cmp(&chan_a.outbound_capacity_msat)
830+
} else {
831+
// Sort in ascending order
832+
chan_a.outbound_capacity_msat.cmp(&chan_b.outbound_capacity_msat)
833+
}
834+
});
835+
}
836+
814837
log_trace!(logger, "Building path from {} (payee) to {} (us/payer) for value {} msat.", payment_params.payee_pubkey, our_node_pubkey, final_value_msat);
815838

816839
macro_rules! add_entry {
@@ -4894,6 +4917,32 @@ mod tests {
48944917
assert_eq!(route.paths[1][0].short_channel_id, 2);
48954918
assert_eq!(route.paths[1][0].fee_msat, 50_000);
48964919
}
4920+
4921+
{
4922+
// If we have a bunch of outbound channels to the same node, where most are not
4923+
// sufficient to pay the full payment, but one is, we should default to just using the
4924+
// one single channel that has sufficient balance, avoiding MPP.
4925+
//
4926+
// If we have several options above the 3xpayment value threshold, we should pick the
4927+
// smallest of them, avoiding further fragmenting our available outbound balance to
4928+
// this node.
4929+
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), Some(&[
4930+
&get_channel_details(Some(2), nodes[0], InitFeatures::known(), 50_000),
4931+
&get_channel_details(Some(3), nodes[0], InitFeatures::known(), 50_000),
4932+
&get_channel_details(Some(5), nodes[0], InitFeatures::known(), 50_000),
4933+
&get_channel_details(Some(6), nodes[0], InitFeatures::known(), 300_000),
4934+
&get_channel_details(Some(7), nodes[0], InitFeatures::known(), 50_000),
4935+
&get_channel_details(Some(8), nodes[0], InitFeatures::known(), 50_000),
4936+
&get_channel_details(Some(9), nodes[0], InitFeatures::known(), 50_000),
4937+
&get_channel_details(Some(4), nodes[0], InitFeatures::known(), 1_000_000),
4938+
]), 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
4939+
assert_eq!(route.paths.len(), 1);
4940+
assert_eq!(route.paths[0].len(), 1);
4941+
4942+
assert_eq!(route.paths[0][0].pubkey, nodes[0]);
4943+
assert_eq!(route.paths[0][0].short_channel_id, 6);
4944+
assert_eq!(route.paths[0][0].fee_msat, 100_000);
4945+
}
48974946
}
48984947

48994948
#[test]

0 commit comments

Comments
 (0)