@@ -225,6 +225,21 @@ pub struct PaymentParameters {
225
225
/// The maximum number of paths that may be used by (MPP) payments.
226
226
/// Defaults to [`DEFAULT_MAX_PATH_COUNT`].
227
227
pub max_path_count : u8 ,
228
+
229
+ /// Selects the maximum share of a channel's total capacity which will be sent over a channel,
230
+ /// as a power of 1/2. A higher value prefers to send the payment using more MPP parts whereas
231
+ /// a lower value prefers to send larger MPP parts, potentially saturating channels and
232
+ /// increasing failure probability for those paths.
233
+ ///
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
+ ///
238
+ /// A value of 0 will allow payments up to and including a channel's total announced usable
239
+ /// capacity, a value of one will only use up to half its capacity, two 1/4, etc.
240
+ ///
241
+ /// Default value: 1
242
+ pub max_channel_saturation_power_of_half : u8 ,
228
243
}
229
244
230
245
impl_writeable_tlv_based ! ( PaymentParameters , {
@@ -233,6 +248,7 @@ impl_writeable_tlv_based!(PaymentParameters, {
233
248
( 2 , features, option) ,
234
249
( 3 , max_path_count, ( default_value, DEFAULT_MAX_PATH_COUNT ) ) ,
235
250
( 4 , route_hints, vec_type) ,
251
+ ( 5 , max_channel_saturation_power_of_half, ( default_value, 1 ) ) ,
236
252
( 6 , expiry_time, option) ,
237
253
} ) ;
238
254
@@ -246,6 +262,7 @@ impl PaymentParameters {
246
262
expiry_time : None ,
247
263
max_total_cltv_expiry_delta : DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA ,
248
264
max_path_count : DEFAULT_MAX_PATH_COUNT ,
265
+ max_channel_saturation_power_of_half : 1 ,
249
266
}
250
267
}
251
268
@@ -288,6 +305,13 @@ impl PaymentParameters {
288
305
pub fn with_max_path_count ( self , max_path_count : u8 ) -> Self {
289
306
Self { max_path_count, ..self }
290
307
}
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
+ }
291
315
}
292
316
293
317
/// A list of hops along a payment path terminating with a channel to the recipient.
@@ -433,16 +457,6 @@ impl<'a> CandidateRouteHop<'a> {
433
457
}
434
458
}
435
459
436
- fn htlc_maximum_msat ( & self ) -> u64 {
437
- match self {
438
- CandidateRouteHop :: FirstHop { details } => details. next_outbound_htlc_limit_msat ,
439
- CandidateRouteHop :: PublicHop { info, .. } => info. htlc_maximum_msat ( ) ,
440
- CandidateRouteHop :: PrivateHop { hint } => {
441
- hint. htlc_maximum_msat . unwrap_or ( u64:: max_value ( ) )
442
- } ,
443
- }
444
- }
445
-
446
460
fn fees ( & self ) -> RoutingFees {
447
461
match self {
448
462
CandidateRouteHop :: FirstHop { .. } => RoutingFees {
@@ -464,6 +478,33 @@ impl<'a> CandidateRouteHop<'a> {
464
478
}
465
479
}
466
480
481
+ #[ inline]
482
+ fn max_htlc_from_capacity ( capacity : EffectiveCapacity , max_channel_saturation_power_of_half : u8 ) -> u64 {
483
+ let saturation_shift: u32 = max_channel_saturation_power_of_half as u32 ;
484
+ match capacity {
485
+ EffectiveCapacity :: ExactLiquidity { liquidity_msat } => liquidity_msat,
486
+ EffectiveCapacity :: Infinite => u64:: max_value ( ) ,
487
+ EffectiveCapacity :: Unknown => EffectiveCapacity :: Unknown . as_msat ( ) ,
488
+ EffectiveCapacity :: MaximumHTLC { amount_msat } =>
489
+ amount_msat. checked_shr ( saturation_shift) . unwrap_or ( 0 ) ,
490
+ EffectiveCapacity :: Total { capacity_msat, htlc_maximum_msat : None } =>
491
+ capacity_msat. checked_shr ( saturation_shift) . unwrap_or ( 0 ) ,
492
+ EffectiveCapacity :: Total { capacity_msat, htlc_maximum_msat : Some ( htlc_max) } =>
493
+ cmp:: min ( capacity_msat. checked_shr ( saturation_shift) . unwrap_or ( 0 ) , htlc_max) ,
494
+ }
495
+ }
496
+
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
+
467
508
/// It's useful to keep track of the hops associated with the fees required to use them,
468
509
/// so that we can choose cheaper paths (as per Dijkstra's algorithm).
469
510
/// Fee values should be updated only in the context of the whole path, see update_value_and_recompute_fees.
@@ -571,10 +612,9 @@ impl<'a> PaymentPath<'a> {
571
612
// to the fees being paid not lining up with the actual limits.
572
613
//
573
614
// Note that this function is not aware of the available_liquidity limit, and thus does not
574
- // support increasing the value being transferred.
615
+ // support increasing the value being transferred beyond what was selected during the initial
616
+ // routing passes.
575
617
fn update_value_and_recompute_fees ( & mut self , value_msat : u64 ) {
576
- assert ! ( value_msat <= self . hops. last( ) . unwrap( ) . 0 . fee_msat) ;
577
-
578
618
let mut total_fee_paid_msat = 0 as u64 ;
579
619
for i in ( 0 ..self . hops . len ( ) ) . rev ( ) {
580
620
let last_hop = i == self . hops . len ( ) - 1 ;
@@ -882,6 +922,11 @@ where L::Target: Logger {
882
922
final_value_msat
883
923
} ;
884
924
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
+
885
930
// Keep track of how much liquidity has been used in selected channels. Used to determine
886
931
// if the channel can be used by additional MPP paths or to inform path finding decisions. It is
887
932
// aware of direction *only* to ensure that the correct htlc_maximum_msat value is used. Hence,
@@ -934,7 +979,8 @@ where L::Target: Logger {
934
979
// - for first and last hops early in get_route
935
980
if $src_node_id != $dest_node_id {
936
981
let short_channel_id = $candidate. short_channel_id( ) ;
937
- let htlc_maximum_msat = $candidate. htlc_maximum_msat( ) ;
982
+ let effective_capacity = $candidate. effective_capacity( ) ;
983
+ let htlc_maximum_msat = max_htlc_from_capacity( effective_capacity, channel_saturation_pow_half) ;
938
984
939
985
// It is tricky to subtract $next_hops_fee_msat from available liquidity here.
940
986
// It may be misleading because we might later choose to reduce the value transferred
@@ -1084,7 +1130,7 @@ where L::Target: Logger {
1084
1130
let channel_usage = ChannelUsage {
1085
1131
amount_msat: amount_to_transfer_over_msat,
1086
1132
inflight_htlc_msat: used_liquidity_msat,
1087
- effective_capacity: $candidate . effective_capacity ( ) ,
1133
+ effective_capacity,
1088
1134
} ;
1089
1135
let channel_penalty_msat = scorer. channel_penalty_msat(
1090
1136
short_channel_id, & $src_node_id, & $dest_node_id, channel_usage
@@ -1505,12 +1551,14 @@ where L::Target: Logger {
1505
1551
. entry ( ( hop. candidate . short_channel_id ( ) , * prev_hop < hop. node_id ) )
1506
1552
. and_modify ( |used_liquidity_msat| * used_liquidity_msat += spent_on_hop_msat)
1507
1553
. or_insert ( spent_on_hop_msat) ;
1508
- if * used_liquidity_msat == hop. candidate . htlc_maximum_msat ( ) {
1554
+ let hop_capacity = hop. candidate . effective_capacity ( ) ;
1555
+ let hop_max_msat = max_htlc_from_capacity ( hop_capacity, channel_saturation_pow_half) ;
1556
+ if * used_liquidity_msat == hop_max_msat {
1509
1557
// If this path used all of this channel's available liquidity, we know
1510
1558
// this path will not be selected again in the next loop iteration.
1511
1559
prevented_redundant_path_selection = true ;
1512
1560
}
1513
- debug_assert ! ( * used_liquidity_msat <= hop . candidate . htlc_maximum_msat ( ) ) ;
1561
+ debug_assert ! ( * used_liquidity_msat <= hop_max_msat ) ;
1514
1562
}
1515
1563
if !prevented_redundant_path_selection {
1516
1564
// If we weren't capped by hitting a liquidity limit on a channel in the path,
@@ -1551,6 +1599,10 @@ where L::Target: Logger {
1551
1599
}
1552
1600
1553
1601
if !allow_mpp {
1602
+ if !found_new_path && channel_saturation_pow_half != 0 {
1603
+ channel_saturation_pow_half = 0 ;
1604
+ continue ' paths_collection;
1605
+ }
1554
1606
// If we don't support MPP, no use trying to gather more value ever.
1555
1607
break ' paths_collection;
1556
1608
}
@@ -1560,7 +1612,9 @@ where L::Target: Logger {
1560
1612
// iteration.
1561
1613
// In the latter case, making another path finding attempt won't help,
1562
1614
// because we deterministically terminated the search due to low liquidity.
1563
- 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 {
1564
1618
log_trace ! ( logger, "Have now collected {} msat (seeking {} msat) in paths. Last path loop {} a new path." ,
1565
1619
already_collected_value_msat, recommended_value_msat, if found_new_path { "found" } else { "did not find" } ) ;
1566
1620
break ' paths_collection;
@@ -1676,8 +1730,32 @@ where L::Target: Logger {
1676
1730
// Step (9).
1677
1731
// Select the best route by lowest total cost.
1678
1732
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
+
1679
1757
let mut selected_paths = Vec :: < Vec < Result < RouteHop , LightningError > > > :: new ( ) ;
1680
- for payment_path in drawn_routes . first ( ) . unwrap ( ) {
1758
+ for payment_path in selected_route {
1681
1759
let mut path = payment_path. hops . iter ( ) . map ( |( payment_hop, node_features) | {
1682
1760
Ok ( RouteHop {
1683
1761
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 ) } ) ?,
@@ -4763,17 +4841,18 @@ mod tests {
4763
4841
4764
4842
// Get a route for 100 sats and check that we found the MPP route no problem and didn't
4765
4843
// overpay at all.
4766
- 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 ( ) ;
4767
4845
assert_eq ! ( route. paths. len( ) , 2 ) ;
4768
- // Paths are somewhat randomly ordered, but:
4769
- // * the first is channel 2 (1 msat fee) -> channel 4 -> channel 42
4770
- // * the second is channel 1 (0 fee, but 99 sat maximum) -> channel 3 -> channel 42
4771
- assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 2 ) ;
4772
- assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 1 ) ;
4773
- assert_eq ! ( route. paths[ 0 ] [ 2 ] . fee_msat, 1_000 ) ;
4774
- assert_eq ! ( route. paths[ 1 ] [ 0 ] . short_channel_id, 1 ) ;
4775
- assert_eq ! ( route. paths[ 1 ] [ 0 ] . fee_msat, 0 ) ;
4776
- 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 ) ;
4777
4856
assert_eq ! ( route. get_total_fees( ) , 1 ) ;
4778
4857
assert_eq ! ( route. get_total_amount( ) , 100_000 ) ;
4779
4858
}
@@ -4787,7 +4866,8 @@ mod tests {
4787
4866
let scorer = test_utils:: TestScorer :: with_penalty ( 0 ) ;
4788
4867
let keys_manager = test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
4789
4868
let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
4790
- 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 ) ;
4791
4871
4792
4872
// We need a route consisting of 3 paths:
4793
4873
// From our node to node2 via node0, node7, node1 (three paths one hop each).
@@ -5235,12 +5315,13 @@ mod tests {
5235
5315
assert_eq ! ( route. paths[ 0 ] . len( ) , 1 ) ;
5236
5316
assert_eq ! ( route. paths[ 1 ] . len( ) , 1 ) ;
5237
5317
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
+
5238
5321
assert_eq ! ( route. paths[ 0 ] [ 0 ] . pubkey, nodes[ 0 ] ) ;
5239
- assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 3 ) ;
5240
5322
assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 50_000 ) ;
5241
5323
5242
5324
assert_eq ! ( route. paths[ 1 ] [ 0 ] . pubkey, nodes[ 0 ] ) ;
5243
- assert_eq ! ( route. paths[ 1 ] [ 0 ] . short_channel_id, 2 ) ;
5244
5325
assert_eq ! ( route. paths[ 1 ] [ 0 ] . fee_msat, 50_000 ) ;
5245
5326
}
5246
5327
@@ -5641,6 +5722,50 @@ mod tests {
5641
5722
}
5642
5723
}
5643
5724
5725
+ #[ test]
5726
+ fn avoids_saturating_channels ( ) {
5727
+ let ( secp_ctx, network_graph, gossip_sync, _, logger) = build_graph ( ) ;
5728
+ let ( _, our_id, privkeys, nodes) = get_nodes ( & secp_ctx) ;
5729
+
5730
+ let scorer = ProbabilisticScorer :: new ( Default :: default ( ) , & * network_graph, Arc :: clone ( & logger) ) ;
5731
+
5732
+ // Set the fee on channel 13 to 100% to match channel 4 giving us two equivalent paths (us
5733
+ // -> node 7 -> node2 and us -> node 1 -> node 2) which we should balance over.
5734
+ update_channel ( & gossip_sync, & secp_ctx, & privkeys[ 1 ] , UnsignedChannelUpdate {
5735
+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
5736
+ short_channel_id : 4 ,
5737
+ timestamp : 2 ,
5738
+ flags : 0 ,
5739
+ cltv_expiry_delta : ( 4 << 4 ) | 1 ,
5740
+ htlc_minimum_msat : 0 ,
5741
+ htlc_maximum_msat : OptionalField :: Present ( 200_000_000 ) ,
5742
+ fee_base_msat : 0 ,
5743
+ fee_proportional_millionths : 0 ,
5744
+ excess_data : Vec :: new ( )
5745
+ } ) ;
5746
+ update_channel ( & gossip_sync, & secp_ctx, & privkeys[ 7 ] , UnsignedChannelUpdate {
5747
+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
5748
+ short_channel_id : 13 ,
5749
+ timestamp : 2 ,
5750
+ flags : 0 ,
5751
+ cltv_expiry_delta : ( 13 << 4 ) | 1 ,
5752
+ htlc_minimum_msat : 0 ,
5753
+ htlc_maximum_msat : OptionalField :: Present ( 200_000_000 ) ,
5754
+ fee_base_msat : 0 ,
5755
+ fee_proportional_millionths : 0 ,
5756
+ excess_data : Vec :: new ( )
5757
+ } ) ;
5758
+
5759
+ let payment_params = PaymentParameters :: from_node_id ( nodes[ 2 ] ) . with_features ( InvoiceFeatures :: known ( ) ) ;
5760
+ let keys_manager = test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
5761
+ let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
5762
+ // 150,000 sat is less than the available liquidity on each channel, set above.
5763
+ let route = get_route ( & our_id, & payment_params, & network_graph. read_only ( ) , None , 150_000_000 , 42 , Arc :: clone ( & logger) , & scorer, & random_seed_bytes) . unwrap ( ) ;
5764
+ assert_eq ! ( route. paths. len( ) , 2 ) ;
5765
+ assert ! ( ( route. paths[ 0 ] [ 1 ] . short_channel_id == 4 && route. paths[ 1 ] [ 1 ] . short_channel_id == 13 ) ||
5766
+ ( route. paths[ 1 ] [ 1 ] . short_channel_id == 4 && route. paths[ 0 ] [ 1 ] . short_channel_id == 13 ) ) ;
5767
+ }
5768
+
5644
5769
#[ cfg( not( feature = "no-std" ) ) ]
5645
5770
pub ( super ) fn random_init_seed ( ) -> u64 {
5646
5771
// Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.
0 commit comments