@@ -575,7 +575,7 @@ impl HistoricalBucketRangeTracker {
575575 // Ensure the bucket index is in the range [0, 7], even if the liquidity offset is zero or
576576 // the channel's capacity, though the second should generally never happen.
577577 debug_assert ! ( liquidity_offset_msat <= capacity_msat) ;
578- let bucket_idx: u8 = ( liquidity_offset_msat. saturating_sub ( 1 ) * 8 / capacity_msat)
578+ let bucket_idx: u8 = ( liquidity_offset_msat * 8 / capacity_msat. saturating_add ( 1 ) )
579579 . try_into ( ) . unwrap_or ( 32 ) ; // 32 is bogus for 8 buckets, and will be ignored
580580 debug_assert ! ( bucket_idx < 8 ) ;
581581 if bucket_idx < 8 {
@@ -1034,12 +1034,12 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
10341034 if params. historical_liquidity_penalty_multiplier_msat != 0 ||
10351035 params. historical_liquidity_penalty_amount_multiplier_msat != 0 {
10361036 let payment_amt_64th_bucket = if amount_msat < u64:: max_value ( ) / 64 {
1037- amount_msat * 64 / self . capacity_msat
1037+ amount_msat * 64 / self . capacity_msat . saturating_add ( 1 )
10381038 } else {
10391039 // Only use 128-bit arithmetic when multiplication will overflow to avoid 128-bit
10401040 // division. This branch should only be hit in fuzz testing since the amount would
10411041 // need to be over 2.88 million BTC in practice.
1042- ( ( amount_msat as u128 ) * 64 / ( self . capacity_msat as u128 ) )
1042+ ( ( amount_msat as u128 ) * 64 / ( self . capacity_msat as u128 ) . saturating_add ( 1 ) )
10431043 . try_into ( ) . unwrap_or ( 65 )
10441044 } ;
10451045 #[ cfg( not( fuzzing) ) ]
@@ -1817,13 +1817,13 @@ mod tests {
18171817 let chain_source: Option < & crate :: util:: test_utils:: TestChainSource > = None ;
18181818 network_graph. update_channel_from_announcement (
18191819 & signed_announcement, & chain_source) . unwrap ( ) ;
1820- update_channel ( network_graph, short_channel_id, node_1_key, 0 ) ;
1821- update_channel ( network_graph, short_channel_id, node_2_key, 1 ) ;
1820+ update_channel ( network_graph, short_channel_id, node_1_key, 0 , 1_000 ) ;
1821+ update_channel ( network_graph, short_channel_id, node_2_key, 1 , 0 ) ;
18221822 }
18231823
18241824 fn update_channel (
18251825 network_graph : & mut NetworkGraph < & TestLogger > , short_channel_id : u64 , node_key : SecretKey ,
1826- flags : u8
1826+ flags : u8 , htlc_maximum_msat : u64
18271827 ) {
18281828 let genesis_hash = genesis_block ( Network :: Testnet ) . header . block_hash ( ) ;
18291829 let secp_ctx = Secp256k1 :: new ( ) ;
@@ -1834,7 +1834,7 @@ mod tests {
18341834 flags,
18351835 cltv_expiry_delta : 18 ,
18361836 htlc_minimum_msat : 0 ,
1837- htlc_maximum_msat : 1_000 ,
1837+ htlc_maximum_msat,
18381838 fee_base_msat : 1 ,
18391839 fee_proportional_millionths : 0 ,
18401840 excess_data : Vec :: new ( ) ,
@@ -2754,6 +2754,7 @@ mod tests {
27542754 let logger = TestLogger :: new ( ) ;
27552755 let network_graph = network_graph ( & logger) ;
27562756 let params = ProbabilisticScoringParameters {
2757+ liquidity_offset_half_life : Duration :: from_secs ( 60 * 60 ) ,
27572758 historical_liquidity_penalty_multiplier_msat : 1024 ,
27582759 historical_liquidity_penalty_amount_multiplier_msat : 1024 ,
27592760 historical_no_updates_half_life : Duration :: from_secs ( 10 ) ,
@@ -2804,6 +2805,25 @@ mod tests {
28042805 } ;
28052806 scorer. payment_path_failed ( & payment_path_for_amount ( 1 ) . iter ( ) . collect :: < Vec < _ > > ( ) , 42 ) ;
28062807 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage) , 409 ) ;
2808+
2809+ let usage = ChannelUsage {
2810+ amount_msat : 1 ,
2811+ inflight_htlc_msat : 0 ,
2812+ effective_capacity : EffectiveCapacity :: MaximumHTLC { amount_msat : 0 } ,
2813+ } ;
2814+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & target, & source, usage) , 2048 ) ;
2815+
2816+ // Advance to decay all liquidity offsets to zero.
2817+ SinceEpoch :: advance ( Duration :: from_secs ( 60 * 60 * 10 ) ) ;
2818+
2819+ // Use a path in the opposite direction, which have zero for htlc_maximum_msat. This will
2820+ // ensure that the effective capacity is zero to test division-by-zero edge cases.
2821+ let path = vec ! [
2822+ path_hop( target_pubkey( ) , 43 , 2 ) ,
2823+ path_hop( source_pubkey( ) , 42 , 1 ) ,
2824+ path_hop( sender_pubkey( ) , 41 , 0 ) ,
2825+ ] ;
2826+ scorer. payment_path_failed ( & path. iter ( ) . collect :: < Vec < _ > > ( ) , 42 ) ;
28072827 }
28082828
28092829 #[ test]
0 commit comments