@@ -2145,11 +2145,10 @@ impl<'a> PaymentPath<'a> {
21452145 let ( next_hops_aggregated_base, next_hops_aggregated_prop) =
21462146 crate :: blinded_path:: payment:: compute_aggregated_base_prop_fee ( next_hops_feerates_iter) . unwrap ( ) ;
21472147
2148- // ceil (((hop_max_msat - agg_base) * 1_000_000) / (1_000_000 + agg_prop))
2148+ // floor (((hop_max_msat - agg_base) * 1_000_000) / (1_000_000 + agg_prop))
21492149 let hop_max_final_value_contribution = ( hop_max_msat as u128 )
21502150 . checked_sub ( next_hops_aggregated_base as u128 )
21512151 . and_then ( |f| f. checked_mul ( 1_000_000 ) )
2152- . and_then ( |f| f. checked_add ( 1_000_000 - 1 ) )
21532152 . and_then ( |f| f. checked_add ( next_hops_aggregated_prop as u128 ) )
21542153 . map ( |f| f / ( ( next_hops_aggregated_prop as u128 ) . saturating_add ( 1_000_000 ) ) ) ;
21552154
@@ -8662,6 +8661,99 @@ mod tests {
86628661 assert_eq ! ( route. get_total_fees( ) , 123 ) ;
86638662 }
86648663
8664+ #[ test]
8665+ fn test_max_final_contribution ( ) {
8666+ // When `compute_max_final_value_contribution` was added, it had a bug where it would
8667+ // over-estimate the maximum value contribution of a hop by using `ceil` rather than
8668+ // `floor`. This tests that case by attempting to send 1 million sats over a channel where
8669+ // the remaining hops have a base fee of zero and a proportional fee of 1 millionth.
8670+
8671+ let ( secp_ctx, network_graph, gossip_sync, _, logger) = build_graph ( ) ;
8672+ let ( our_privkey, our_id, privkeys, nodes) = get_nodes ( & secp_ctx) ;
8673+ let scorer = ln_test_utils:: TestScorer :: new ( ) ;
8674+ let random_seed_bytes = [ 42 ; 32 ] ;
8675+
8676+ // Enable channel 1, setting max HTLC to 1M sats
8677+ update_channel ( & gossip_sync, & secp_ctx, & our_privkey, UnsignedChannelUpdate {
8678+ chain_hash : ChainHash :: using_genesis_block ( Network :: Testnet ) ,
8679+ short_channel_id : 1 ,
8680+ timestamp : 2 ,
8681+ message_flags : 1 , // Only must_be_one
8682+ channel_flags : 0 ,
8683+ cltv_expiry_delta : ( 1 << 4 ) | 1 ,
8684+ htlc_minimum_msat : 0 ,
8685+ htlc_maximum_msat : 1_000_000 ,
8686+ fee_base_msat : 0 ,
8687+ fee_proportional_millionths : 0 ,
8688+ excess_data : Vec :: new ( )
8689+ } ) ;
8690+
8691+ // Set the fee on channel 3 to zero
8692+ update_channel ( & gossip_sync, & secp_ctx, & privkeys[ 0 ] , UnsignedChannelUpdate {
8693+ chain_hash : ChainHash :: using_genesis_block ( Network :: Testnet ) ,
8694+ short_channel_id : 3 ,
8695+ timestamp : 2 ,
8696+ message_flags : 1 , // Only must_be_one
8697+ channel_flags : 0 ,
8698+ cltv_expiry_delta : ( 3 << 4 ) | 1 ,
8699+ htlc_minimum_msat : 0 ,
8700+ htlc_maximum_msat : 1_000_000_000 ,
8701+ fee_base_msat : 0 ,
8702+ fee_proportional_millionths : 0 ,
8703+ excess_data : Vec :: new ( )
8704+ } ) ;
8705+
8706+ // Set the fee on channel 6 to 1 millionth
8707+ update_channel ( & gossip_sync, & secp_ctx, & privkeys[ 2 ] , UnsignedChannelUpdate {
8708+ chain_hash : ChainHash :: using_genesis_block ( Network :: Testnet ) ,
8709+ short_channel_id : 6 ,
8710+ timestamp : 2 ,
8711+ message_flags : 1 , // Only must_be_one
8712+ channel_flags : 0 ,
8713+ cltv_expiry_delta : ( 6 << 4 ) | 1 ,
8714+ htlc_minimum_msat : 0 ,
8715+ htlc_maximum_msat : 1_000_000_000 ,
8716+ fee_base_msat : 0 ,
8717+ fee_proportional_millionths : 1 ,
8718+ excess_data : Vec :: new ( )
8719+ } ) ;
8720+
8721+ // Now attempt to pay over the channel 1 -> channel 3 -> channel 6 path
8722+ // This should fail as we need to send 1M + 1 sats to cover the fee but channel 1 only
8723+ // allows for 1M sats to flow over it.
8724+ let config = UserConfig :: default ( ) ;
8725+ let payment_params = PaymentParameters :: from_node_id ( nodes[ 4 ] , 42 )
8726+ . with_bolt11_features ( channelmanager:: provided_bolt11_invoice_features ( & config) )
8727+ . unwrap ( ) ;
8728+ let route_params = RouteParameters :: from_payment_params_and_value ( payment_params, 1_000_000 ) ;
8729+ get_route ( & our_id, & route_params, & network_graph. read_only ( ) , None ,
8730+ Arc :: clone ( & logger) , & scorer, & Default :: default ( ) , & random_seed_bytes) . unwrap_err ( ) ;
8731+
8732+ // Now set channel 1 max HTLC to 1M + 1 sats
8733+ update_channel ( & gossip_sync, & secp_ctx, & our_privkey, UnsignedChannelUpdate {
8734+ chain_hash : ChainHash :: using_genesis_block ( Network :: Testnet ) ,
8735+ short_channel_id : 1 ,
8736+ timestamp : 3 ,
8737+ message_flags : 1 , // Only must_be_one
8738+ channel_flags : 0 ,
8739+ cltv_expiry_delta : ( 1 << 4 ) | 1 ,
8740+ htlc_minimum_msat : 0 ,
8741+ htlc_maximum_msat : 1_000_001 ,
8742+ fee_base_msat : 0 ,
8743+ fee_proportional_millionths : 0 ,
8744+ excess_data : Vec :: new ( )
8745+ } ) ;
8746+
8747+ // And attempt the same payment again, but this time it should work.
8748+ let route = get_route ( & our_id, & route_params, & network_graph. read_only ( ) , None ,
8749+ Arc :: clone ( & logger) , & scorer, & Default :: default ( ) , & random_seed_bytes) . unwrap ( ) ;
8750+ assert_eq ! ( route. paths. len( ) , 1 ) ;
8751+ assert_eq ! ( route. paths[ 0 ] . hops. len( ) , 3 ) ;
8752+ assert_eq ! ( route. paths[ 0 ] . hops[ 0 ] . short_channel_id, 1 ) ;
8753+ assert_eq ! ( route. paths[ 0 ] . hops[ 1 ] . short_channel_id, 3 ) ;
8754+ assert_eq ! ( route. paths[ 0 ] . hops[ 2 ] . short_channel_id, 6 ) ;
8755+ }
8756+
86658757 #[ test]
86668758 fn allow_us_being_first_hint ( ) {
86678759 // Check that we consider a route hint even if we are the src of the first hop.
0 commit comments