Skip to content

Commit 9af04d5

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 4a21a6c commit 9af04d5

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
@@ -230,6 +230,10 @@ pub struct PaymentParameters {
230230
/// channel, as a power of 1/2. A higher value prefers to send the payment using more MPP parts
231231
/// whereas a lower value prefers to send larger MPP parts, potentially saturating channels.
232232
///
233+
/// Note that this restriction will be relaxed during pathfinding after paths which meet this
234+
/// restriction have been found. While paths which meet this criteria will be searched for, it
235+
/// is ultimately up to the scorer to select them over other paths.
236+
///
233237
/// A value of 0 will allow payments up to and including a channel's total announced usable
234238
/// capacity, a value of one will only use up to half its capacity, two 1/4, etc.
235239
///
@@ -257,10 +261,6 @@ impl PaymentParameters {
257261
expiry_time: None,
258262
max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
259263
max_path_count: DEFAULT_MAX_PATH_COUNT,
260-
#[cfg(test)] // Many tests were written prior to the introduction of this parameter, so
261-
// we leave it as 0 by default in tests, and change it for a few.
262-
max_channel_saturation_power_of_half: 0,
263-
#[cfg(not(test))]
264264
max_channel_saturation_power_of_half: 1,
265265
}
266266
}
@@ -304,6 +304,13 @@ impl PaymentParameters {
304304
pub fn with_max_path_count(self, max_path_count: u8) -> Self {
305305
Self { max_path_count, ..self }
306306
}
307+
308+
/// Includes a limit for the maximum number of payment paths that may be used.
309+
///
310+
/// (C-not exported) since bindings don't support move semantics
311+
pub fn with_max_channel_saturation_power_of_half(self, max_channel_saturation_power_of_half: u8) -> Self {
312+
Self { max_channel_saturation_power_of_half, ..self }
313+
}
307314
}
308315

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

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

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

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

15811602
if !allow_mpp {
1603+
if !found_new_path && channel_saturation_pow_half != 0 {
1604+
channel_saturation_pow_half = 0;
1605+
continue 'paths_collection;
1606+
}
15821607
// If we don't support MPP, no use trying to gather more value ever.
15831608
break 'paths_collection;
15841609
}
@@ -1588,7 +1613,9 @@ where L::Target: Logger {
15881613
// iteration.
15891614
// In the latter case, making another path finding attempt won't help,
15901615
// because we deterministically terminated the search due to low liquidity.
1591-
if already_collected_value_msat >= recommended_value_msat || !found_new_path {
1616+
if !found_new_path && channel_saturation_pow_half != 0 {
1617+
channel_saturation_pow_half = 0;
1618+
} else if already_collected_value_msat >= recommended_value_msat || !found_new_path {
15921619
log_trace!(logger, "Have now collected {} msat (seeking {} msat) in paths. Last path loop {} a new path.",
15931620
already_collected_value_msat, recommended_value_msat, if found_new_path { "found" } else { "did not find" });
15941621
break 'paths_collection;
@@ -1704,8 +1731,32 @@ where L::Target: Logger {
17041731
// Step (9).
17051732
// Select the best route by lowest total cost.
17061733
drawn_routes.sort_unstable_by_key(|paths| paths.iter().map(|path| path.get_cost_msat()).sum::<u64>());
1734+
let selected_route = drawn_routes.first_mut().unwrap();
1735+
1736+
// Sort by the path itself and combine redundant paths.
1737+
// Note that we sort by SCIDs alone as its simpler but when combining we have to ensure we
1738+
// compare both SCIDs and NodeIds as individual nodes may use random aliases causing collisions
1739+
// across nodes.
1740+
selected_route.sort_unstable_by_key(|path| {
1741+
let mut key = [0u64; MAX_PATH_LENGTH_ESTIMATE as usize];
1742+
debug_assert!(path.hops.len() <= key.len());
1743+
for (scid, key) in path.hops.iter().map(|h| h.0.candidate.short_channel_id()).zip(key.iter_mut()) {
1744+
*key = scid;
1745+
}
1746+
key
1747+
});
1748+
for idx in 0..(selected_route.len() - 1) {
1749+
if idx + 1 >= selected_route.len() { break; }
1750+
if iter_equal(selected_route[idx ].hops.iter().map(|h: &(PathBuildingHop<'_>, _)| (h.0.candidate.short_channel_id(), h.0.node_id)),
1751+
selected_route[idx + 1].hops.iter().map(|h: &(PathBuildingHop<'_>, _)| (h.0.candidate.short_channel_id(), h.0.node_id))) {
1752+
let new_value = selected_route[idx].get_value_msat() + selected_route[idx + 1].get_value_msat();
1753+
selected_route[idx].update_value_and_recompute_fees(new_value);
1754+
selected_route.remove(idx + 1);
1755+
}
1756+
}
1757+
17071758
let mut selected_paths = Vec::<Vec<Result<RouteHop, LightningError>>>::new();
1708-
for payment_path in drawn_routes.first().unwrap() {
1759+
for payment_path in selected_route {
17091760
let mut path = payment_path.hops.iter().map(|(payment_hop, node_features)| {
17101761
Ok(RouteHop {
17111762
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)})?,
@@ -4791,17 +4842,18 @@ mod tests {
47914842

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

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

5319+
assert!((route.paths[0][0].short_channel_id == 3 && route.paths[1][0].short_channel_id == 2) ||
5320+
(route.paths[0][0].short_channel_id == 2 && route.paths[1][0].short_channel_id == 3));
5321+
52665322
assert_eq!(route.paths[0][0].pubkey, nodes[0]);
5267-
assert_eq!(route.paths[0][0].short_channel_id, 3);
52685323
assert_eq!(route.paths[0][0].fee_msat, 50_000);
52695324

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

0 commit comments

Comments
 (0)