@@ -2145,11 +2145,10 @@ impl<'a> PaymentPath<'a> {
2145
2145
let ( next_hops_aggregated_base, next_hops_aggregated_prop) =
2146
2146
crate :: blinded_path:: payment:: compute_aggregated_base_prop_fee ( next_hops_feerates_iter) . unwrap ( ) ;
2147
2147
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))
2149
2149
let hop_max_final_value_contribution = ( hop_max_msat as u128 )
2150
2150
. checked_sub ( next_hops_aggregated_base as u128 )
2151
2151
. and_then ( |f| f. checked_mul ( 1_000_000 ) )
2152
- . and_then ( |f| f. checked_add ( 1_000_000 - 1 ) )
2153
2152
. and_then ( |f| f. checked_add ( next_hops_aggregated_prop as u128 ) )
2154
2153
. map ( |f| f / ( ( next_hops_aggregated_prop as u128 ) . saturating_add ( 1_000_000 ) ) ) ;
2155
2154
@@ -8662,6 +8661,99 @@ mod tests {
8662
8661
assert_eq ! ( route. get_total_fees( ) , 123 ) ;
8663
8662
}
8664
8663
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
+
8665
8757
#[ test]
8666
8758
fn allow_us_being_first_hint ( ) {
8667
8759
// Check that we consider a route hint even if we are the src of the first hop.
0 commit comments