@@ -1113,10 +1113,16 @@ 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+ // We want a value of final_value_msat * ROUTE_CAPACITY_PROVISION_FACTOR but we
1117+ // need it to increment at each hop by the fee charged at later hops. Further,
1118+ // we need to ensure we round up when we divide to get satoshis.
1119+ let channel_cap_msat = final_value_msat
1120+ . checked_mul ( ROUTE_CAPACITY_PROVISION_FACTOR ) . and_then ( |v| v. checked_add ( aggregate_next_hops_fee_msat) )
1121+ . unwrap_or ( u64:: max_value ( ) ) ;
1122+ let channel_cap_sat = match channel_cap_msat. checked_add ( 999 ) {
1123+ None => break , // We overflowed above, just ignore this route hint
1124+ Some ( val) => Some ( val / 1000 ) ,
1125+ } ;
11201126
11211127 let src_node_id = NodeId :: from_pubkey ( & hop. src_node_id ) ;
11221128 let dest_node_id = NodeId :: from_pubkey ( & prev_hop_id) ;
@@ -1128,7 +1134,7 @@ where L::Target: Logger {
11281134 // sufficient value to route `final_value_msat`. Note that in the case of "0-value"
11291135 // invoices where the invoice does not specify value this may not be the case, but
11301136 // better to include the hints than not.
1131- if !add_entry ! ( hop. short_channel_id, src_node_id, dest_node_id, directional_info, reqd_channel_cap , & empty_channel_features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat) {
1137+ if !add_entry ! ( hop. short_channel_id, src_node_id, dest_node_id, directional_info, channel_cap_sat , & empty_channel_features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat) {
11321138 // If this hop was not used then there is no use checking the preceding hops
11331139 // in the RouteHint. We can break by just searching for a direct channel between
11341140 // last checked hop and first_hop_targets
@@ -4055,7 +4061,105 @@ mod tests {
40554061 assert_eq ! ( total_amount_paid_msat, 200_000 ) ;
40564062 assert_eq ! ( route. get_total_fees( ) , 150_000 ) ;
40574063 }
4064+ }
40584065
4066+ #[ test]
4067+ fn mpp_with_last_hops ( ) {
4068+ // Previously, if we tried to send an MPP payment to a destination which was only reachable
4069+ // via a single last-hop route hint, we'd fail to route if we first collected routes
4070+ // totaling close but not quite enough to fund the full payment.
4071+ //
4072+ // This was because we considered last-hop hints to have exactly the sought payment amount
4073+ // instead of the amount we were trying to collect, needlessly limiting our path searching
4074+ // at the very first hop.
4075+ //
4076+ // Specifically, this interacted with our "all paths must fund at least 5% of total target"
4077+ // criterion to cause us to refuse all routes at the last hop hint which would be considered
4078+ // to only have the remaining to-collect amount in available liquidity.
4079+ //
4080+ // This bug appeared in production in some specific channel configurations.
4081+ let ( secp_ctx, network_graph, net_graph_msg_handler, _, logger) = build_graph ( ) ;
4082+ let ( our_privkey, our_id, privkeys, nodes) = get_nodes ( & secp_ctx) ;
4083+ let scorer = test_utils:: TestScorer :: with_fixed_penalty ( 0 ) ;
4084+ let payee = Payee :: from_node_id ( PublicKey :: from_slice ( & [ 02 ; 33 ] ) . unwrap ( ) ) . with_features ( InvoiceFeatures :: known ( ) )
4085+ . with_route_hints ( vec ! [ RouteHint ( vec![ RouteHintHop {
4086+ src_node_id: nodes[ 2 ] ,
4087+ short_channel_id: 42 ,
4088+ fees: RoutingFees { base_msat: 0 , proportional_millionths: 0 } ,
4089+ cltv_expiry_delta: 42 ,
4090+ htlc_minimum_msat: None ,
4091+ htlc_maximum_msat: None ,
4092+ } ] ) ] ) ;
4093+
4094+ // Keep only two paths from us to nodes[2], both with a 99sat HTLC maximum, with one with
4095+ // no fee and one with a 1msat fee. Previously, trying to route 100 sats to nodes[2] here
4096+ // would first use the no-fee route and then fail to find a path along the second route as
4097+ // we think we can only send up to 1 additional sat over the last-hop but refuse to as its
4098+ // under 5% of our payment amount.
4099+ update_channel ( & net_graph_msg_handler, & secp_ctx, & our_privkey, UnsignedChannelUpdate {
4100+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
4101+ short_channel_id : 1 ,
4102+ timestamp : 2 ,
4103+ flags : 0 ,
4104+ cltv_expiry_delta : u16:: max_value ( ) ,
4105+ htlc_minimum_msat : 0 ,
4106+ htlc_maximum_msat : OptionalField :: Present ( 99_000 ) ,
4107+ fee_base_msat : u32:: max_value ( ) ,
4108+ fee_proportional_millionths : u32:: max_value ( ) ,
4109+ excess_data : Vec :: new ( )
4110+ } ) ;
4111+ update_channel ( & net_graph_msg_handler, & secp_ctx, & our_privkey, UnsignedChannelUpdate {
4112+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
4113+ short_channel_id : 2 ,
4114+ timestamp : 2 ,
4115+ flags : 0 ,
4116+ cltv_expiry_delta : u16:: max_value ( ) ,
4117+ htlc_minimum_msat : 0 ,
4118+ htlc_maximum_msat : OptionalField :: Present ( 99_000 ) ,
4119+ fee_base_msat : u32:: max_value ( ) ,
4120+ fee_proportional_millionths : u32:: max_value ( ) ,
4121+ excess_data : Vec :: new ( )
4122+ } ) ;
4123+ update_channel ( & net_graph_msg_handler, & secp_ctx, & privkeys[ 1 ] , UnsignedChannelUpdate {
4124+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
4125+ short_channel_id : 4 ,
4126+ timestamp : 2 ,
4127+ flags : 0 ,
4128+ cltv_expiry_delta : ( 4 << 8 ) | 1 ,
4129+ htlc_minimum_msat : 0 ,
4130+ htlc_maximum_msat : OptionalField :: Absent ,
4131+ fee_base_msat : 1 ,
4132+ fee_proportional_millionths : 0 ,
4133+ excess_data : Vec :: new ( )
4134+ } ) ;
4135+ update_channel ( & net_graph_msg_handler, & secp_ctx, & privkeys[ 7 ] , UnsignedChannelUpdate {
4136+ chain_hash : genesis_block ( Network :: Testnet ) . header . block_hash ( ) ,
4137+ short_channel_id : 13 ,
4138+ timestamp : 2 ,
4139+ flags : 0 |2 , // Channel disabled
4140+ cltv_expiry_delta : ( 13 << 8 ) | 1 ,
4141+ htlc_minimum_msat : 0 ,
4142+ htlc_maximum_msat : OptionalField :: Absent ,
4143+ fee_base_msat : 0 ,
4144+ fee_proportional_millionths : 2000000 ,
4145+ excess_data : Vec :: new ( )
4146+ } ) ;
4147+
4148+ // Get a route for 100 sats and check that we found the MPP route no problem and didn't
4149+ // overpay at all.
4150+ let route = get_route ( & our_id, & payee, & network_graph, None , 100_000 , 42 , Arc :: clone ( & logger) , & scorer) . unwrap ( ) ;
4151+ assert_eq ! ( route. paths. len( ) , 2 ) ;
4152+ // Paths are somewhat randomly ordered, but:
4153+ // * the first is channel 2 (1 msat fee) -> channel 4 -> channel 42
4154+ // * the second is channel 1 (0 fee, but 99 sat maximum) -> channel 3 -> channel 42
4155+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . short_channel_id, 2 ) ;
4156+ assert_eq ! ( route. paths[ 0 ] [ 0 ] . fee_msat, 1 ) ;
4157+ assert_eq ! ( route. paths[ 0 ] [ 2 ] . fee_msat, 1_000 ) ;
4158+ assert_eq ! ( route. paths[ 1 ] [ 0 ] . short_channel_id, 1 ) ;
4159+ assert_eq ! ( route. paths[ 1 ] [ 0 ] . fee_msat, 0 ) ;
4160+ assert_eq ! ( route. paths[ 1 ] [ 2 ] . fee_msat, 99_000 ) ;
4161+ assert_eq ! ( route. get_total_fees( ) , 1 ) ;
4162+ assert_eq ! ( route. get_total_amount( ) , 100_000 ) ;
40594163 }
40604164
40614165 #[ test]
0 commit comments