Skip to content

Commit a02982f

Browse files
committed
Relax the channel saturation limit if we can't find enough paths
In order to avoid failing to find paths due to the new channel saturation limit, if we fail to find enough paths, we simply disable the saturation limit for further path finding iterations. Because we can now increase the maximum sent over a given channel during routefinding, we may now generate redundant paths for the same payment. Because this is wasteful in the network, we add an additional pass during routefinding to merge redundant paths. Note that two tests which previously attempted to send exactly the available liquidity over a channel which charged an absolute fee need updating - in those cases the router will first collect a path that is saturation-limited, then attempt to collect a second path without a saturation limit while stil honoring the existing utilized capacity on the channel, causing failure as the absolute fee must be included.
1 parent bd6b710 commit a02982f

File tree

3 files changed

+90
-28
lines changed

3 files changed

+90
-28
lines changed

lightning/src/ln/functional_tests.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,9 +1823,12 @@ fn test_channel_reserve_holding_cell_htlcs() {
18231823

18241824
// attempt to send amt_msat > their_max_htlc_value_in_flight_msat
18251825
{
1826-
let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], recv_value_0);
1826+
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id())
1827+
.with_features(InvoiceFeatures::known()).with_max_channel_saturation_power_of_half(0);
1828+
let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, recv_value_0, TEST_FINAL_CLTV);
18271829
route.paths[0].last_mut().unwrap().fee_msat += 1;
18281830
assert!(route.paths[0].iter().rev().skip(1).all(|h| h.fee_msat == feemsat));
1831+
18291832
unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)), true, APIError::ChannelUnavailable { ref err },
18301833
assert!(regex::Regex::new(r"Cannot send value that would put us over the max HTLC value in flight our peer will accept \(\d+\)").unwrap().is_match(err)));
18311834
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
@@ -1844,7 +1847,12 @@ fn test_channel_reserve_holding_cell_htlcs() {
18441847
if stat01.value_to_self_msat < stat01.channel_reserve_msat + commit_tx_fee_all_htlcs + ensure_htlc_amounts_above_dust_buffer + amt_msat {
18451848
break;
18461849
}
1847-
send_payment(&nodes[0], &vec![&nodes[1], &nodes[2]][..], recv_value_0);
1850+
1851+
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id())
1852+
.with_features(InvoiceFeatures::known()).with_max_channel_saturation_power_of_half(0);
1853+
let route = get_route!(nodes[0], payment_params, recv_value_0, TEST_FINAL_CLTV).unwrap();
1854+
let (payment_preimage, ..) = send_along_route(&nodes[0], route, &[&nodes[1], &nodes[2]], recv_value_0);
1855+
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
18481856

18491857
let (stat01_, stat11_, stat12_, stat22_) = (
18501858
get_channel_value_stat!(nodes[0], chan_1.2),

lightning/src/ln/payment_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,7 @@ fn failed_probe_yields_event() {
942942

943943
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id());
944944

945-
let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], &payment_params, 9_999_000, 42);
945+
let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], &payment_params, 9_998_000, 42);
946946

947947
let (payment_hash, payment_id) = nodes[0].node.send_probe(route.paths[0].clone()).unwrap();
948948

lightning/src/routing/router.rs

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ pub struct PaymentParameters {
231231
/// a lower value prefers to send larger MPP parts, potentially saturating channels and
232232
/// increasing failure probability for those paths.
233233
///
234+
/// Note that this restriction will be relaxed during pathfinding after paths which meet this
235+
/// restriction have been found. While paths which meet this criteria will be searched for, it
236+
/// is ultimately up to the scorer to select them over other paths.
237+
///
234238
/// A value of 0 will allow payments up to and including a channel's total announced usable
235239
/// capacity, a value of one will only use up to half its capacity, two 1/4, etc.
236240
///
@@ -258,10 +262,6 @@ impl PaymentParameters {
258262
expiry_time: None,
259263
max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
260264
max_path_count: DEFAULT_MAX_PATH_COUNT,
261-
#[cfg(test)] // Many tests were written prior to the introduction of this parameter, so
262-
// we leave it as 0 by default in tests, and change it for a few.
263-
max_channel_saturation_power_of_half: 0,
264-
#[cfg(not(test))]
265265
max_channel_saturation_power_of_half: 1,
266266
}
267267
}
@@ -305,6 +305,13 @@ impl PaymentParameters {
305305
pub fn with_max_path_count(self, max_path_count: u8) -> Self {
306306
Self { max_path_count, ..self }
307307
}
308+
309+
/// Includes a limit for the maximum number of payment paths that may be used.
310+
///
311+
/// (C-not exported) since bindings don't support move semantics
312+
pub fn with_max_channel_saturation_power_of_half(self, max_channel_saturation_power_of_half: u8) -> Self {
313+
Self { max_channel_saturation_power_of_half, ..self }
314+
}
308315
}
309316

310317
/// A list of hops along a payment path terminating with a channel to the recipient.
@@ -487,6 +494,17 @@ fn max_htlc_from_capacity(capacity: EffectiveCapacity, max_channel_saturation_po
487494
}
488495
}
489496

497+
fn iter_equal<I1: Iterator, I2: Iterator>(mut iter_a: I1, mut iter_b: I2)
498+
-> bool where I1::Item: PartialEq<I2::Item> {
499+
loop {
500+
let a = iter_a.next();
501+
let b = iter_b.next();
502+
if a.is_none() && b.is_none() { return true; }
503+
if a.is_none() || b.is_none() { return false; }
504+
if a.unwrap().ne(&b.unwrap()) { return false; }
505+
}
506+
}
507+
490508
/// It's useful to keep track of the hops associated with the fees required to use them,
491509
/// so that we can choose cheaper paths (as per Dijkstra's algorithm).
492510
/// Fee values should be updated only in the context of the whole path, see update_value_and_recompute_fees.
@@ -594,10 +612,9 @@ impl<'a> PaymentPath<'a> {
594612
// to the fees being paid not lining up with the actual limits.
595613
//
596614
// Note that this function is not aware of the available_liquidity limit, and thus does not
597-
// support increasing the value being transferred.
615+
// support increasing the value being transferred beyond what was selected during the initial
616+
// routing passes.
598617
fn update_value_and_recompute_fees(&mut self, value_msat: u64) {
599-
assert!(value_msat <= self.hops.last().unwrap().0.fee_msat);
600-
601618
let mut total_fee_paid_msat = 0 as u64;
602619
for i in (0..self.hops.len()).rev() {
603620
let last_hop = i == self.hops.len() - 1;
@@ -905,6 +922,11 @@ where L::Target: Logger {
905922
final_value_msat
906923
};
907924

925+
// When we start collecting routes we enforce the max_channel_saturation_power_of_half
926+
// requirement strictly. After we've collected enough (or if we fail to find new routes) we
927+
// drop the requirement by setting this to 0.
928+
let mut channel_saturation_pow_half = payment_params.max_channel_saturation_power_of_half;
929+
908930
// Keep track of how much liquidity has been used in selected channels. Used to determine
909931
// if the channel can be used by additional MPP paths or to inform path finding decisions. It is
910932
// aware of direction *only* to ensure that the correct htlc_maximum_msat value is used. Hence,
@@ -958,7 +980,7 @@ where L::Target: Logger {
958980
if $src_node_id != $dest_node_id {
959981
let short_channel_id = $candidate.short_channel_id();
960982
let effective_capacity = $candidate.effective_capacity();
961-
let htlc_maximum_msat = max_htlc_from_capacity(effective_capacity, payment_params.max_channel_saturation_power_of_half);
983+
let htlc_maximum_msat = max_htlc_from_capacity(effective_capacity, channel_saturation_pow_half);
962984

963985
// It is tricky to subtract $next_hops_fee_msat from available liquidity here.
964986
// It may be misleading because we might later choose to reduce the value transferred
@@ -1530,8 +1552,7 @@ where L::Target: Logger {
15301552
.and_modify(|used_liquidity_msat| *used_liquidity_msat += spent_on_hop_msat)
15311553
.or_insert(spent_on_hop_msat);
15321554
let hop_capacity = hop.candidate.effective_capacity();
1533-
let hop_max_msat = max_htlc_from_capacity(hop_capacity,
1534-
payment_params.max_channel_saturation_power_of_half);
1555+
let hop_max_msat = max_htlc_from_capacity(hop_capacity, channel_saturation_pow_half);
15351556
if *used_liquidity_msat == hop_max_msat {
15361557
// If this path used all of this channel's available liquidity, we know
15371558
// this path will not be selected again in the next loop iteration.
@@ -1578,6 +1599,10 @@ where L::Target: Logger {
15781599
}
15791600

15801601
if !allow_mpp {
1602+
if !found_new_path && channel_saturation_pow_half != 0 {
1603+
channel_saturation_pow_half = 0;
1604+
continue 'paths_collection;
1605+
}
15811606
// If we don't support MPP, no use trying to gather more value ever.
15821607
break 'paths_collection;
15831608
}
@@ -1587,7 +1612,9 @@ where L::Target: Logger {
15871612
// iteration.
15881613
// In the latter case, making another path finding attempt won't help,
15891614
// because we deterministically terminated the search due to low liquidity.
1590-
if already_collected_value_msat >= recommended_value_msat || !found_new_path {
1615+
if !found_new_path && channel_saturation_pow_half != 0 {
1616+
channel_saturation_pow_half = 0;
1617+
} else if already_collected_value_msat >= recommended_value_msat || !found_new_path {
15911618
log_trace!(logger, "Have now collected {} msat (seeking {} msat) in paths. Last path loop {} a new path.",
15921619
already_collected_value_msat, recommended_value_msat, if found_new_path { "found" } else { "did not find" });
15931620
break 'paths_collection;
@@ -1703,8 +1730,32 @@ where L::Target: Logger {
17031730
// Step (9).
17041731
// Select the best route by lowest total cost.
17051732
drawn_routes.sort_unstable_by_key(|paths| paths.iter().map(|path| path.get_cost_msat()).sum::<u64>());
1733+
let selected_route = drawn_routes.first_mut().unwrap();
1734+
1735+
// Sort by the path itself and combine redundant paths.
1736+
// Note that we sort by SCIDs alone as its simpler but when combining we have to ensure we
1737+
// compare both SCIDs and NodeIds as individual nodes may use random aliases causing collisions
1738+
// across nodes.
1739+
selected_route.sort_unstable_by_key(|path| {
1740+
let mut key = [0u64; MAX_PATH_LENGTH_ESTIMATE as usize];
1741+
debug_assert!(path.hops.len() <= key.len());
1742+
for (scid, key) in path.hops.iter().map(|h| h.0.candidate.short_channel_id()).zip(key.iter_mut()) {
1743+
*key = scid;
1744+
}
1745+
key
1746+
});
1747+
for idx in 0..(selected_route.len() - 1) {
1748+
if idx + 1 >= selected_route.len() { break; }
1749+
if iter_equal(selected_route[idx ].hops.iter().map(|h| (h.0.candidate.short_channel_id(), h.0.node_id)),
1750+
selected_route[idx + 1].hops.iter().map(|h| (h.0.candidate.short_channel_id(), h.0.node_id))) {
1751+
let new_value = selected_route[idx].get_value_msat() + selected_route[idx + 1].get_value_msat();
1752+
selected_route[idx].update_value_and_recompute_fees(new_value);
1753+
selected_route.remove(idx + 1);
1754+
}
1755+
}
1756+
17061757
let mut selected_paths = Vec::<Vec<Result<RouteHop, LightningError>>>::new();
1707-
for payment_path in drawn_routes.first().unwrap() {
1758+
for payment_path in selected_route {
17081759
let mut path = payment_path.hops.iter().map(|(payment_hop, node_features)| {
17091760
Ok(RouteHop {
17101761
pubkey: PublicKey::from_slice(payment_hop.node_id.as_slice()).map_err(|_| LightningError{err: format!("Public key {:?} is invalid", &payment_hop.node_id), action: ErrorAction::IgnoreAndLog(Level::Trace)})?,
@@ -4790,17 +4841,18 @@ mod tests {
47904841

47914842
// Get a route for 100 sats and check that we found the MPP route no problem and didn't
47924843
// overpay at all.
4793-
let route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
4844+
let mut route = get_route(&our_id, &payment_params, &network_graph.read_only(), None, 100_000, 42, Arc::clone(&logger), &scorer, &random_seed_bytes).unwrap();
47944845
assert_eq!(route.paths.len(), 2);
4795-
// Paths are somewhat randomly ordered, but:
4796-
// * the first is channel 2 (1 msat fee) -> channel 4 -> channel 42
4797-
// * the second is channel 1 (0 fee, but 99 sat maximum) -> channel 3 -> channel 42
4798-
assert_eq!(route.paths[0][0].short_channel_id, 2);
4799-
assert_eq!(route.paths[0][0].fee_msat, 1);
4800-
assert_eq!(route.paths[0][2].fee_msat, 1_000);
4801-
assert_eq!(route.paths[1][0].short_channel_id, 1);
4802-
assert_eq!(route.paths[1][0].fee_msat, 0);
4803-
assert_eq!(route.paths[1][2].fee_msat, 99_000);
4846+
route.paths.sort_by_key(|path| path[0].short_channel_id);
4847+
// Paths are manually ordered ordered by SCID, so:
4848+
// * the first is channel 1 (0 fee, but 99 sat maximum) -> channel 3 -> channel 42
4849+
// * the second is channel 2 (1 msat fee) -> channel 4 -> channel 42
4850+
assert_eq!(route.paths[0][0].short_channel_id, 1);
4851+
assert_eq!(route.paths[0][0].fee_msat, 0);
4852+
assert_eq!(route.paths[0][2].fee_msat, 99_000);
4853+
assert_eq!(route.paths[1][0].short_channel_id, 2);
4854+
assert_eq!(route.paths[1][0].fee_msat, 1);
4855+
assert_eq!(route.paths[1][2].fee_msat, 1_000);
48044856
assert_eq!(route.get_total_fees(), 1);
48054857
assert_eq!(route.get_total_amount(), 100_000);
48064858
}
@@ -4814,7 +4866,8 @@ mod tests {
48144866
let scorer = test_utils::TestScorer::with_penalty(0);
48154867
let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
48164868
let random_seed_bytes = keys_manager.get_secure_random_bytes();
4817-
let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known());
4869+
let payment_params = PaymentParameters::from_node_id(nodes[2]).with_features(InvoiceFeatures::known())
4870+
.with_max_channel_saturation_power_of_half(0);
48184871

48194872
// We need a route consisting of 3 paths:
48204873
// From our node to node2 via node0, node7, node1 (three paths one hop each).
@@ -5262,12 +5315,13 @@ mod tests {
52625315
assert_eq!(route.paths[0].len(), 1);
52635316
assert_eq!(route.paths[1].len(), 1);
52645317

5318+
assert!((route.paths[0][0].short_channel_id == 3 && route.paths[1][0].short_channel_id == 2) ||
5319+
(route.paths[0][0].short_channel_id == 2 && route.paths[1][0].short_channel_id == 3));
5320+
52655321
assert_eq!(route.paths[0][0].pubkey, nodes[0]);
5266-
assert_eq!(route.paths[0][0].short_channel_id, 3);
52675322
assert_eq!(route.paths[0][0].fee_msat, 50_000);
52685323

52695324
assert_eq!(route.paths[1][0].pubkey, nodes[0]);
5270-
assert_eq!(route.paths[1][0].short_channel_id, 2);
52715325
assert_eq!(route.paths[1][0].fee_msat, 50_000);
52725326
}
52735327

0 commit comments

Comments
 (0)