@@ -176,6 +176,11 @@ impl_writeable_tlv_based!(RouteParameters, {
176
176
/// Maximum total CTLV difference we allow for a full payment path.
177
177
pub const DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA : u32 = 1008 ;
178
178
179
+ /// Maximum number of paths we allow an MPP payment to have.
180
+ // The default limit is currently set rather arbitrary - there aren't any real fundamental path-count
181
+ // limits, but for now more than 10 paths likely carries too much one-path failure.
182
+ pub const DEFAULT_MAX_MPP_PATH_COUNT : u8 = 10 ;
183
+
179
184
// The median hop CLTV expiry delta currently seen in the network.
180
185
const MEDIAN_HOP_CLTV_EXPIRY_DELTA : u32 = 40 ;
181
186
@@ -214,13 +219,19 @@ pub struct PaymentParameters {
214
219
pub expiry_time : Option < u64 > ,
215
220
216
221
/// The maximum total CLTV delta we accept for the route.
222
+ /// Defaults to [`DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA`].
217
223
pub max_total_cltv_expiry_delta : u32 ,
224
+
225
+ /// The maximum number of paths that may be used by MPP payments.
226
+ /// Defaults to [`DEFAULT_MAX_MPP_PATH_COUNT`].
227
+ pub max_mpp_path_count : u8 ,
218
228
}
219
229
220
230
impl_writeable_tlv_based ! ( PaymentParameters , {
221
231
( 0 , payee_pubkey, required) ,
222
232
( 1 , max_total_cltv_expiry_delta, ( default_value, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA ) ) ,
223
233
( 2 , features, option) ,
234
+ ( 3 , max_mpp_path_count, ( default_value, DEFAULT_MAX_MPP_PATH_COUNT ) ) ,
224
235
( 4 , route_hints, vec_type) ,
225
236
( 6 , expiry_time, option) ,
226
237
} ) ;
@@ -234,6 +245,7 @@ impl PaymentParameters {
234
245
route_hints : vec ! [ ] ,
235
246
expiry_time : None ,
236
247
max_total_cltv_expiry_delta : DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA ,
248
+ max_mpp_path_count : DEFAULT_MAX_MPP_PATH_COUNT ,
237
249
}
238
250
}
239
251
@@ -269,6 +281,13 @@ impl PaymentParameters {
269
281
pub fn with_max_total_cltv_expiry_delta ( self , max_total_cltv_expiry_delta : u32 ) -> Self {
270
282
Self { max_total_cltv_expiry_delta, ..self }
271
283
}
284
+
285
+ /// Includes a limit for the maximum number of payment paths that may be used by MPP.
286
+ ///
287
+ /// (C-not exported) since bindings don't support move semantics
288
+ pub fn with_max_mpp_path_count ( self , max_mpp_path_count : u8 ) -> Self {
289
+ Self { max_mpp_path_count, ..self }
290
+ }
272
291
}
273
292
274
293
/// A list of hops along a payment path terminating with a channel to the recipient.
@@ -790,6 +809,11 @@ where L::Target: Logger {
790
809
node_info. features . supports_basic_mpp ( )
791
810
} else { false }
792
811
} else { false } ;
812
+
813
+ if allow_mpp && payment_params. max_mpp_path_count == 0 {
814
+ return Err ( LightningError { err : "Can't find an MPP route with no paths allowed." . to_owned ( ) , action : ErrorAction :: IgnoreError } ) ;
815
+ }
816
+
793
817
log_trace ! ( logger, "Searching for a route from payer {} to payee {} {} MPP and {} first hops {}overriding the network graph" , our_node_pubkey,
794
818
payment_params. payee_pubkey, if allow_mpp { "with" } else { "without" } ,
795
819
first_hops. map( |hops| hops. len( ) ) . unwrap_or( 0 ) , if first_hops. is_some( ) { "" } else { "not " } ) ;
@@ -840,18 +864,30 @@ where L::Target: Logger {
840
864
let recommended_value_msat = final_value_msat * ROUTE_CAPACITY_PROVISION_FACTOR as u64 ;
841
865
let mut path_value_msat = final_value_msat;
842
866
867
+ // Routing Fragmentation Mitigation heuristic:
868
+ //
869
+ // Routing fragmentation across many payment paths increases the overall routing
870
+ // fees as you have irreducible routing fees per-link used (`fee_base_msat`).
871
+ // Taking too many smaller paths also increases the chance of payment failure.
872
+ // Thus to avoid this effect, we require from our collected links to provide
873
+ // at least a minimal contribution to the recommended value yet-to-be-fulfilled.
874
+ // This requirement is currently set to be 1/max_mpp_path_count of the payment
875
+ // value to ensure we only ever return routes that do not violate this limit.
876
+ let minimal_value_contribution_msat: u64 = if allow_mpp {
877
+ ( final_value_msat + ( payment_params. max_mpp_path_count as u64 - 1 ) ) / payment_params. max_mpp_path_count as u64
878
+ } else {
879
+ final_value_msat
880
+ } ;
881
+
843
882
// Keep track of how much liquidity has been used in selected channels. Used to determine
844
883
// if the channel can be used by additional MPP paths or to inform path finding decisions. It is
845
884
// aware of direction *only* to ensure that the correct htlc_maximum_msat value is used. Hence,
846
885
// liquidity used in one direction will not offset any used in the opposite direction.
847
886
let mut used_channel_liquidities: HashMap < ( u64 , bool ) , u64 > =
848
887
HashMap :: with_capacity ( network_nodes. len ( ) ) ;
849
888
850
- // Keeping track of how much value we already collected across other paths. Helps to decide:
851
- // - how much a new path should be transferring (upper bound);
852
- // - whether a channel should be disregarded because
853
- // it's available liquidity is too small comparing to how much more we need to collect;
854
- // - when we want to stop looking for new paths.
889
+ // Keeping track of how much value we already collected across other paths. Helps to decide
890
+ // when we want to stop looking for new paths.
855
891
let mut already_collected_value_msat = 0 ;
856
892
857
893
for ( _, channels) in first_hop_targets. iter_mut ( ) {
@@ -913,26 +949,6 @@ where L::Target: Logger {
913
949
* used_liquidity_msat
914
950
} ) ;
915
951
916
- // Routing Fragmentation Mitigation heuristic:
917
- //
918
- // Routing fragmentation across many payment paths increases the overall routing
919
- // fees as you have irreducible routing fees per-link used (`fee_base_msat`).
920
- // Taking too many smaller paths also increases the chance of payment failure.
921
- // Thus to avoid this effect, we require from our collected links to provide
922
- // at least a minimal contribution to the recommended value yet-to-be-fulfilled.
923
- //
924
- // This requirement is currently 5% of the remaining-to-be-collected value.
925
- // This means as we successfully advance in our collection,
926
- // the absolute liquidity contribution is lowered,
927
- // thus increasing the number of potential channels to be selected.
928
-
929
- // Derive the minimal liquidity contribution with a ratio of 20 (5%, rounded up)
930
- // or 100% if we're not allowed to do multipath payments.
931
- let minimal_value_contribution_msat: u64 = if allow_mpp {
932
- ( recommended_value_msat - already_collected_value_msat + 19 ) / 20
933
- } else {
934
- final_value_msat
935
- } ;
936
952
// Verify the liquidity offered by this channel complies to the minimal contribution.
937
953
let contributes_sufficient_value = available_value_contribution_msat >= minimal_value_contribution_msat;
938
954
// Do not consider candidate hops that would exceed the maximum path length.
@@ -1504,10 +1520,8 @@ where L::Target: Logger {
1504
1520
* used_channel_liquidities. entry ( ( victim_scid, true ) ) . or_default ( ) = exhausted;
1505
1521
}
1506
1522
1507
- // Track the total amount all our collected paths allow to send so that we:
1508
- // - know when to stop looking for more paths
1509
- // - know which of the hops are useless considering how much more sats we need
1510
- // (contributes_sufficient_value)
1523
+ // Track the total amount all our collected paths allow to send so that we know
1524
+ // when to stop looking for more paths
1511
1525
already_collected_value_msat += value_contribution_msat;
1512
1526
1513
1527
payment_paths. push ( payment_path) ;
@@ -1678,6 +1692,8 @@ where L::Target: Logger {
1678
1692
} ) ;
1679
1693
selected_paths. push ( path) ;
1680
1694
}
1695
+ // Make sure we would never create a route with more paths than we allow.
1696
+ debug_assert ! ( selected_paths. len( ) <= payment_params. max_mpp_path_count. into( ) ) ;
1681
1697
1682
1698
if let Some ( features) = & payment_params. features {
1683
1699
for path in selected_paths. iter_mut ( ) {
@@ -3999,7 +4015,8 @@ mod tests {
3999
4015
let scorer = test_utils:: TestScorer :: with_penalty ( 0 ) ;
4000
4016
let keys_manager = test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
4001
4017
let random_seed_bytes = keys_manager. get_secure_random_bytes ( ) ;
4002
- let payment_params = PaymentParameters :: from_node_id ( nodes[ 2 ] ) . with_features ( InvoiceFeatures :: known ( ) ) ;
4018
+ let payment_params = PaymentParameters :: from_node_id ( nodes[ 2 ] )
4019
+ . with_features ( InvoiceFeatures :: known ( ) ) ;
4003
4020
4004
4021
// We need a route consisting of 3 paths:
4005
4022
// From our node to node2 via node0, node7, node1 (three paths one hop each).
@@ -4092,15 +4109,39 @@ mod tests {
4092
4109
{
4093
4110
// Attempt to route more than available results in a failure.
4094
4111
if let Err ( LightningError { err, action : ErrorAction :: IgnoreError } ) = get_route (
4095
- & our_id, & payment_params, & network_graph. read_only ( ) , None , 300_000 , 42 , Arc :: clone ( & logger) , & scorer, & random_seed_bytes) {
4096
- assert_eq ! ( err, "Failed to find a sufficient route to the given destination" ) ;
4112
+ & our_id, & payment_params, & network_graph. read_only ( ) , None , 300_000 , 42 ,
4113
+ Arc :: clone ( & logger) , & scorer, & random_seed_bytes) {
4114
+ assert_eq ! ( err, "Failed to find a sufficient route to the given destination" ) ;
4115
+ } else { panic ! ( ) ; }
4116
+ }
4117
+
4118
+ {
4119
+ // Attempt to route while setting max_mpp_path_count to 0 results in a failure.
4120
+ let zero_payment_params = payment_params. clone ( ) . with_max_mpp_path_count ( 0 ) ;
4121
+ if let Err ( LightningError { err, action : ErrorAction :: IgnoreError } ) = get_route (
4122
+ & our_id, & zero_payment_params, & network_graph. read_only ( ) , None , 100 , 42 ,
4123
+ Arc :: clone ( & logger) , & scorer, & random_seed_bytes) {
4124
+ assert_eq ! ( err, "Can't find an MPP route with no paths allowed." ) ;
4125
+ } else { panic ! ( ) ; }
4126
+ }
4127
+
4128
+ {
4129
+ // Attempt to route while setting max_mpp_path_count to 3 results in a failure.
4130
+ // This is the case because the minimal_value_contribution_msat would require each path
4131
+ // to account for 1/3 of the total value, which is violated by 2 out of 3 paths.
4132
+ let fail_payment_params = payment_params. clone ( ) . with_max_mpp_path_count ( 3 ) ;
4133
+ if let Err ( LightningError { err, action : ErrorAction :: IgnoreError } ) = get_route (
4134
+ & our_id, & fail_payment_params, & network_graph. read_only ( ) , None , 250_000 , 42 ,
4135
+ Arc :: clone ( & logger) , & scorer, & random_seed_bytes) {
4136
+ assert_eq ! ( err, "Failed to find a sufficient route to the given destination" ) ;
4097
4137
} else { panic ! ( ) ; }
4098
4138
}
4099
4139
4100
4140
{
4101
4141
// Now, attempt to route 250 sats (just a bit below the capacity).
4102
4142
// Our algorithm should provide us with these 3 paths.
4103
- let route = get_route ( & our_id, & payment_params, & network_graph. read_only ( ) , None , 250_000 , 42 , Arc :: clone ( & logger) , & scorer, & random_seed_bytes) . unwrap ( ) ;
4143
+ let route = get_route ( & our_id, & payment_params, & network_graph. read_only ( ) , None ,
4144
+ 250_000 , 42 , Arc :: clone ( & logger) , & scorer, & random_seed_bytes) . unwrap ( ) ;
4104
4145
assert_eq ! ( route. paths. len( ) , 3 ) ;
4105
4146
let mut total_amount_paid_msat = 0 ;
4106
4147
for path in & route. paths {
@@ -4113,7 +4154,8 @@ mod tests {
4113
4154
4114
4155
{
4115
4156
// Attempt to route an exact amount is also fine
4116
- let route = get_route ( & our_id, & payment_params, & network_graph. read_only ( ) , None , 290_000 , 42 , Arc :: clone ( & logger) , & scorer, & random_seed_bytes) . unwrap ( ) ;
4157
+ let route = get_route ( & our_id, & payment_params, & network_graph. read_only ( ) , None ,
4158
+ 290_000 , 42 , Arc :: clone ( & logger) , & scorer, & random_seed_bytes) . unwrap ( ) ;
4117
4159
assert_eq ! ( route. paths. len( ) , 3 ) ;
4118
4160
let mut total_amount_paid_msat = 0 ;
4119
4161
for path in & route. paths {
0 commit comments