Skip to content

Commit 9c556c5

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 4869a01 commit 9c556c5

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.
@@ -486,6 +493,17 @@ fn max_htlc_from_capacity(capacity: EffectiveCapacity, max_channel_saturation_po
486493
}
487494
}
488495

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

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

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

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

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

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

5317+
assert!((route.paths[0][0].short_channel_id == 3 && route.paths[1][0].short_channel_id == 2) ||
5318+
(route.paths[0][0].short_channel_id == 2 && route.paths[1][0].short_channel_id == 3));
5319+
52645320
assert_eq!(route.paths[0][0].pubkey, nodes[0]);
5265-
assert_eq!(route.paths[0][0].short_channel_id, 3);
52665321
assert_eq!(route.paths[0][0].fee_msat, 50_000);
52675322

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

0 commit comments

Comments
 (0)