@@ -1113,10 +1113,16 @@ where L::Target: Logger {
1113
1113
fees : hop. fees ,
1114
1114
} ;
1115
1115
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
+ } ;
1120
1126
1121
1127
let src_node_id = NodeId :: from_pubkey ( & hop. src_node_id ) ;
1122
1128
let dest_node_id = NodeId :: from_pubkey ( & prev_hop_id) ;
@@ -1128,7 +1134,7 @@ where L::Target: Logger {
1128
1134
// sufficient value to route `final_value_msat`. Note that in the case of "0-value"
1129
1135
// invoices where the invoice does not specify value this may not be the case, but
1130
1136
// 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) {
1132
1138
// If this hop was not used then there is no use checking the preceding hops
1133
1139
// in the RouteHint. We can break by just searching for a direct channel between
1134
1140
// last checked hop and first_hop_targets
@@ -4055,7 +4061,105 @@ mod tests {
4055
4061
assert_eq ! ( total_amount_paid_msat, 200_000 ) ;
4056
4062
assert_eq ! ( route. get_total_fees( ) , 150_000 ) ;
4057
4063
}
4064
+ }
4058
4065
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 ) ;
4059
4163
}
4060
4164
4061
4165
#[ test]
0 commit comments