@@ -1113,10 +1113,13 @@ where L::Target: Logger {
11131113 fees : hop. fees ,
11141114 } ;
11151115
1116- let reqd_channel_cap = if let Some ( val) = final_value_msat. checked_add ( match idx {
1117- 0 => 999 ,
1118- _ => aggregate_next_hops_fee_msat. checked_add ( 999 ) . unwrap_or ( u64:: max_value ( ) )
1119- } ) { Some ( val / 1000 ) } else { break ; } ; // converting from msat or breaking if max ~ infinity
1116+ let reqd_channel_cap = if let Some ( val) = final_value_msat
1117+ . checked_mul ( ROUTE_CAPACITY_PROVISION_FACTOR ) . unwrap_or ( u64:: max_value ( ) )
1118+ . checked_add ( match idx {
1119+ 0 => 999 ,
1120+ _ => aggregate_next_hops_fee_msat. checked_add ( 999 ) . unwrap_or ( u64:: max_value ( ) )
1121+ } )
1122+ { Some ( val / 1000 ) } else { break ; } ; // converting from msat or breaking if max ~ infinity
11201123
11211124 let src_node_id = NodeId :: from_pubkey ( & hop. src_node_id ) ;
11221125 let dest_node_id = NodeId :: from_pubkey ( & prev_hop_id) ;
@@ -4055,7 +4058,98 @@ mod tests {
40554058 assert_eq ! ( total_amount_paid_msat, 200_000 ) ;
40564059 assert_eq ! ( route. get_total_fees( ) , 150_000 ) ;
40574060 }
4061+ }
40584062
4063+ #[ test]
4064+ fn mpp_with_last_hops ( ) {
4065+ // Previously, if we tried to send an MPP payment to a destination which was only reachable
4066+ // via a single last-hop route hint, we'd fail to route if we first collected routes
4067+ // totaling close, but not quite enough to fund the full payment.
4068+ //
4069+ // This was because we considered last-hop hints to have exactly the sought payment amount
4070+ // instead of the amount we were trying to collect, needlessly limiting our path searching
4071+ // at the very first hop.
4072+ //
4073+ // Specifically, this interacted with our "all paths must fund at least 5% of total target"
4074+ // criteria to cause us to refuse all routes at the last hop hint which would be considered
4075+ // to only have the remaining to-collect amount in available liquidity.
4076+ //
4077+ // This bug appeared in production in some specific channel configurations.
4078+ let ( secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph ( ) ;
4079+ let ( our_privkey, our_id, privkeys, nodes) = get_nodes ( & secp_ctx) ;
4080+ let scorer = test_utils:: TestScorer :: with_fixed_penalty ( 0 ) ;
4081+ let payee = Payee :: from_node_id ( PublicKey :: from_slice ( & [ 02 ; 33 ] ) . unwrap ( ) ) . with_features ( InvoiceFeatures :: known ( ) )
4082+ . with_route_hints ( vec ! [ RouteHint ( vec![ RouteHintHop {
4083+ src_node_id: nodes[ 2 ] ,
4084+ short_channel_id: 42 ,
4085+ fees: RoutingFees { base_msat: 0 , proportional_millionths: 0 } ,
4086+ cltv_expiry_delta: 42 ,
4087+ htlc_minimum_msat: None ,
4088+ htlc_maximum_msat: None ,
4089+ } ] ) ] ) ;
4090+
4091+ // Keep only two paths from us to nodes[2], one with no fee and a 90sat HTLC maximum, one
4092+ // with a slight fee and a 20sat *minimum*. Previously, trying to route 100 sats to
4093+ // nodes[2] here would first use the 90sat no-fee route and then fail to find a path along
4094+ // the second route as we think we can only send up to 10 sats over the last-hop but have a
4095+ // minimum of 20 sats.
4096+ update_channel ( & net_graph_msg_handler, & secp_ctx, & our_privkey, UnsignedChannelUpdate {
4097+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
4098+ short_channel_id : 1 ,
4099+ timestamp : 2 ,
4100+ flags : 0 ,
4101+ cltv_expiry_delta : u16:: max_value ( ) ,
4102+ htlc_minimum_msat : 0 ,
4103+ htlc_maximum_msat : OptionalField :: Present ( 99_000 ) ,
4104+ fee_base_msat : u32:: max_value ( ) ,
4105+ fee_proportional_millionths : u32:: max_value ( ) ,
4106+ excess_data : Vec :: new ( )
4107+ } ) ;
4108+ update_channel ( & net_graph_msg_handler, & secp_ctx, & our_privkey, UnsignedChannelUpdate {
4109+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
4110+ short_channel_id : 2 ,
4111+ timestamp : 2 ,
4112+ flags : 0 ,
4113+ cltv_expiry_delta : u16:: max_value ( ) ,
4114+ htlc_minimum_msat : 0 ,
4115+ htlc_maximum_msat : OptionalField :: Present ( 60_000 ) ,
4116+ fee_base_msat : u32:: max_value ( ) ,
4117+ fee_proportional_millionths : u32:: max_value ( ) ,
4118+ excess_data : Vec :: new ( )
4119+ } ) ;
4120+ update_channel ( & net_graph_msg_handler, & secp_ctx, & privkeys[ 1 ] , UnsignedChannelUpdate {
4121+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
4122+ short_channel_id : 4 ,
4123+ timestamp : 2 ,
4124+ flags : 0 ,
4125+ cltv_expiry_delta : ( 4 << 8 ) | 1 ,
4126+ htlc_minimum_msat : 0 ,
4127+ htlc_maximum_msat : OptionalField :: Absent ,
4128+ fee_base_msat : 1 ,
4129+ fee_proportional_millionths : 0 ,
4130+ excess_data : Vec :: new ( )
4131+ } ) ;
4132+ update_channel ( & net_graph_msg_handler, & secp_ctx, & privkeys[ 7 ] , UnsignedChannelUpdate {
4133+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
4134+ short_channel_id : 13 ,
4135+ timestamp : 2 ,
4136+ flags : 0 |2 , // Channel disabled
4137+ cltv_expiry_delta : ( 13 << 8 ) | 1 ,
4138+ htlc_minimum_msat : 0 ,
4139+ htlc_maximum_msat : OptionalField :: Absent ,
4140+ fee_base_msat : 0 ,
4141+ fee_proportional_millionths : 2000000 ,
4142+ excess_data : Vec :: new ( )
4143+ } ) ;
4144+
4145+ // Get a route for 100 sats and check that we found the MPP route no problem and didn't
4146+ // overpay at all.
4147+ let route = get_route ( & our_id, & payee, & network_graph, None , 100_000 , 42 , Arc :: clone ( & logger) , & scorer) . unwrap ( ) ;
4148+ assert_eq ! ( route. paths. len( ) , 2 ) ;
4149+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 2 ) ;
4150+ assert_eq ! ( route. paths[ 1 ] [ 0 ] . short_channel_id, 1 ) ;
4151+ assert_eq ! ( route. get_total_fees( ) , 1 ) ;
4152+ assert_eq ! ( route. get_total_amount( ) , 100_000 ) ;
40594153 }
40604154
40614155 #[ test]
0 commit comments