@@ -176,6 +176,11 @@ impl_writeable_tlv_based!(RouteParameters, {
176176/// Maximum total CTLV difference we allow for a full payment path.
177177pub const DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA : u32 = 1008 ;
178178
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+
179184// The median hop CLTV expiry delta currently seen in the network.
180185const MEDIAN_HOP_CLTV_EXPIRY_DELTA : u32 = 40 ;
181186
@@ -214,13 +219,19 @@ pub struct PaymentParameters {
214219 pub expiry_time : Option < u64 > ,
215220
216221 /// The maximum total CLTV delta we accept for the route.
222+ /// Defaults to [`DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA`].
217223 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 ,
218228}
219229
220230impl_writeable_tlv_based ! ( PaymentParameters , {
221231 ( 0 , payee_pubkey, required) ,
222232 ( 1 , max_total_cltv_expiry_delta, ( default_value, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA ) ) ,
223233 ( 2 , features, option) ,
234+ ( 3 , max_mpp_path_count, ( default_value, DEFAULT_MAX_MPP_PATH_COUNT ) ) ,
224235 ( 4 , route_hints, vec_type) ,
225236 ( 6 , expiry_time, option) ,
226237} ) ;
@@ -234,6 +245,7 @@ impl PaymentParameters {
234245 route_hints : vec ! [ ] ,
235246 expiry_time : None ,
236247 max_total_cltv_expiry_delta : DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA ,
248+ max_mpp_path_count : DEFAULT_MAX_MPP_PATH_COUNT ,
237249 }
238250 }
239251
@@ -269,6 +281,13 @@ impl PaymentParameters {
269281 pub fn with_max_total_cltv_expiry_delta ( self , max_total_cltv_expiry_delta : u32 ) -> Self {
270282 Self { max_total_cltv_expiry_delta, ..self }
271283 }
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+ }
272291}
273292
274293/// A list of hops along a payment path terminating with a channel to the recipient.
@@ -790,6 +809,11 @@ where L::Target: Logger {
790809 node_info. features . supports_basic_mpp ( )
791810 } else { false }
792811 } 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+
793817 log_trace ! ( logger, "Searching for a route from payer {} to payee {} {} MPP and {} first hops {}overriding the network graph" , our_node_pubkey,
794818 payment_params. payee_pubkey, if allow_mpp { "with" } else { "without" } ,
795819 first_hops. map( |hops| hops. len( ) ) . unwrap_or( 0 ) , if first_hops. is_some( ) { "" } else { "not " } ) ;
@@ -840,18 +864,30 @@ where L::Target: Logger {
840864 let recommended_value_msat = final_value_msat * ROUTE_CAPACITY_PROVISION_FACTOR as u64 ;
841865 let mut path_value_msat = final_value_msat;
842866
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+
843882 // Keep track of how much liquidity has been used in selected channels. Used to determine
844883 // if the channel can be used by additional MPP paths or to inform path finding decisions. It is
845884 // aware of direction *only* to ensure that the correct htlc_maximum_msat value is used. Hence,
846885 // liquidity used in one direction will not offset any used in the opposite direction.
847886 let mut used_channel_liquidities: HashMap < ( u64 , bool ) , u64 > =
848887 HashMap :: with_capacity ( network_nodes. len ( ) ) ;
849888
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.
855891 let mut already_collected_value_msat = 0 ;
856892
857893 for ( _, channels) in first_hop_targets. iter_mut ( ) {
@@ -913,26 +949,6 @@ where L::Target: Logger {
913949 * used_liquidity_msat
914950 } ) ;
915951
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- } ;
936952 // Verify the liquidity offered by this channel complies to the minimal contribution.
937953 let contributes_sufficient_value = available_value_contribution_msat >= minimal_value_contribution_msat;
938954 // Do not consider candidate hops that would exceed the maximum path length.
@@ -1504,10 +1520,8 @@ where L::Target: Logger {
15041520 * used_channel_liquidities. entry ( ( victim_scid, true ) ) . or_default ( ) = exhausted;
15051521 }
15061522
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
15111525 already_collected_value_msat += value_contribution_msat;
15121526
15131527 payment_paths. push ( payment_path) ;
@@ -1678,6 +1692,8 @@ where L::Target: Logger {
16781692 } ) ;
16791693 selected_paths. push ( path) ;
16801694 }
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( ) ) ;
16811697
16821698 if let Some ( features) = & payment_params. features {
16831699 for path in selected_paths. iter_mut ( ) {
@@ -3999,7 +4015,8 @@ mod tests {
39994015 let scorer = test_utils:: TestScorer :: with_penalty ( 0 ) ;
40004016 let keys_manager = test_utils:: TestKeysInterface :: new ( & [ 0u8 ; 32 ] , Network :: Testnet ) ;
40014017 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 ( ) ) ;
40034020
40044021 // We need a route consisting of 3 paths:
40054022 // From our node to node2 via node0, node7, node1 (three paths one hop each).
@@ -4092,15 +4109,39 @@ mod tests {
40924109 {
40934110 // Attempt to route more than available results in a failure.
40944111 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" ) ;
40974137 } else { panic ! ( ) ; }
40984138 }
40994139
41004140 {
41014141 // Now, attempt to route 250 sats (just a bit below the capacity).
41024142 // 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 ( ) ;
41044145 assert_eq ! ( route. paths. len( ) , 3 ) ;
41054146 let mut total_amount_paid_msat = 0 ;
41064147 for path in & route. paths {
@@ -4113,7 +4154,8 @@ mod tests {
41134154
41144155 {
41154156 // 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 ( ) ;
41174159 assert_eq ! ( route. paths. len( ) , 3 ) ;
41184160 let mut total_amount_paid_msat = 0 ;
41194161 for path in & route. paths {
0 commit comments