@@ -555,6 +555,12 @@ pub struct ProbabilisticScoringParameters {
555
555
/// When built with the `no-std` feature, time will never elapse. Therefore, the channel
556
556
/// liquidity knowledge will never decay except when the bounds cross.
557
557
pub liquidity_offset_half_life : Duration ,
558
+
559
+ /// Whether an additional penalty should be calculated based on the payment amount in order to
560
+ /// provide higher penalties for large payments.
561
+ ///
562
+ /// Default value: false
563
+ pub include_amount_penalty : bool ,
558
564
}
559
565
560
566
/// Accounting for channel liquidity balance uncertainty.
@@ -608,6 +614,7 @@ impl Default for ProbabilisticScoringParameters {
608
614
base_penalty_msat : 500 ,
609
615
liquidity_penalty_multiplier_msat : 40_000 ,
610
616
liquidity_offset_half_life : Duration :: from_secs ( 3600 ) ,
617
+ include_amount_penalty : false ,
611
618
}
612
619
}
613
620
}
@@ -665,11 +672,17 @@ impl<T: Time> ChannelLiquidity<T> {
665
672
}
666
673
}
667
674
675
+ /// Bounds `-log10` to avoid excessive liquidity penalties for payments with low success
676
+ /// probabilities.
677
+ const NEGATIVE_LOG10_UPPER_BOUND : u64 = 2 ;
678
+
679
+ /// The divisor used when computing the amount penalty.
680
+ const AMOUNT_PENALTY_DIVISOR : u64 = 1024 ;
681
+
668
682
impl < L : Deref < Target = u64 > , T : Time , U : Deref < Target = T > > DirectedChannelLiquidity < L , T , U > {
669
683
/// Returns a penalty for routing the given HTLC `amount_msat` through the channel in this
670
684
/// direction.
671
- fn penalty_msat ( & self , amount_msat : u64 , liquidity_penalty_multiplier_msat : u64 ) -> u64 {
672
- let max_penalty_msat = liquidity_penalty_multiplier_msat. saturating_mul ( 2 ) ;
685
+ fn penalty_msat ( & self , amount_msat : u64 , params : ProbabilisticScoringParameters ) -> u64 {
673
686
let max_liquidity_msat = self . max_liquidity_msat ( ) ;
674
687
let min_liquidity_msat = core:: cmp:: min ( self . min_liquidity_msat ( ) , max_liquidity_msat) ;
675
688
if amount_msat <= min_liquidity_msat {
@@ -681,15 +694,36 @@ impl<L: Deref<Target = u64>, T: Time, U: Deref<Target = T>> DirectedChannelLiqui
681
694
// Avoid using the failed channel on retry.
682
695
u64:: max_value ( )
683
696
} else {
684
- max_penalty_msat
697
+ let negative_log10_times_1024 = NEGATIVE_LOG10_UPPER_BOUND * 1024 ;
698
+ self . combined_penalty_msat ( amount_msat, negative_log10_times_1024, params)
685
699
}
686
700
} else {
687
701
let numerator = ( max_liquidity_msat - amount_msat) . saturating_add ( 1 ) ;
688
702
let denominator = ( max_liquidity_msat - min_liquidity_msat) . saturating_add ( 1 ) ;
689
- let penalty_msat = approx:: negative_log10_times_1024 ( numerator, denominator)
690
- . saturating_mul ( liquidity_penalty_multiplier_msat) / 1024 ;
691
- // Upper bound the penalty to ensure some channel is selected.
692
- penalty_msat. min ( max_penalty_msat)
703
+ let negative_log10_times_1024 =
704
+ approx:: negative_log10_times_1024 ( numerator, denominator) ;
705
+ self . combined_penalty_msat ( amount_msat, negative_log10_times_1024, params)
706
+ }
707
+ }
708
+
709
+ #[ inline]
710
+ fn combined_penalty_msat (
711
+ & self , amount_msat : u64 , negative_log10_times_1024 : u64 ,
712
+ params : ProbabilisticScoringParameters
713
+ ) -> u64 {
714
+ // Upper bound the liquidity penalty to ensure some channel is selected.
715
+ let multiplier_msat = params. liquidity_penalty_multiplier_msat ;
716
+ let max_penalty_msat = multiplier_msat. saturating_mul ( NEGATIVE_LOG10_UPPER_BOUND ) ;
717
+ let penalty_msat = ( negative_log10_times_1024. saturating_mul ( multiplier_msat) / 1024 )
718
+ . min ( max_penalty_msat)
719
+ . saturating_add ( params. base_penalty_msat ) ;
720
+
721
+ if params. include_amount_penalty {
722
+ let amount_penalty = negative_log10_times_1024. saturating_mul ( amount_msat) / 1024
723
+ / AMOUNT_PENALTY_DIVISOR ;
724
+ penalty_msat. saturating_add ( amount_penalty)
725
+ } else {
726
+ penalty_msat
693
727
}
694
728
}
695
729
@@ -762,14 +796,12 @@ impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsin
762
796
& self , short_channel_id : u64 , amount_msat : u64 , capacity_msat : u64 , source : & NodeId ,
763
797
target : & NodeId
764
798
) -> u64 {
765
- let liquidity_penalty_multiplier_msat = self . params . liquidity_penalty_multiplier_msat ;
766
799
let liquidity_offset_half_life = self . params . liquidity_offset_half_life ;
767
800
self . channel_liquidities
768
801
. get ( & short_channel_id)
769
802
. unwrap_or ( & ChannelLiquidity :: new ( ) )
770
803
. as_directed ( source, target, capacity_msat, liquidity_offset_half_life)
771
- . penalty_msat ( amount_msat, liquidity_penalty_multiplier_msat)
772
- . saturating_add ( self . params . base_penalty_msat )
804
+ . penalty_msat ( amount_msat, self . params )
773
805
}
774
806
775
807
fn payment_path_failed ( & mut self , path : & [ & RouteHop ] , short_channel_id : u64 ) {
@@ -1884,6 +1916,7 @@ mod tests {
1884
1916
base_penalty_msat : 0 ,
1885
1917
liquidity_penalty_multiplier_msat : 1_000 ,
1886
1918
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
1919
+ ..Default :: default ( )
1887
1920
} ;
1888
1921
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1889
1922
let source = source_node_id ( ) ;
@@ -1936,6 +1969,7 @@ mod tests {
1936
1969
base_penalty_msat : 0 ,
1937
1970
liquidity_penalty_multiplier_msat : 1_000 ,
1938
1971
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
1972
+ ..Default :: default ( )
1939
1973
} ;
1940
1974
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1941
1975
let source = source_node_id ( ) ;
@@ -1961,6 +1995,7 @@ mod tests {
1961
1995
base_penalty_msat : 0 ,
1962
1996
liquidity_penalty_multiplier_msat : 1_000 ,
1963
1997
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
1998
+ ..Default :: default ( )
1964
1999
} ;
1965
2000
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1966
2001
let source = source_node_id ( ) ;
@@ -1999,6 +2034,7 @@ mod tests {
1999
2034
base_penalty_msat : 0 ,
2000
2035
liquidity_penalty_multiplier_msat : 1_000 ,
2001
2036
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2037
+ ..Default :: default ( )
2002
2038
} ;
2003
2039
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2004
2040
let source = source_node_id ( ) ;
@@ -2029,6 +2065,7 @@ mod tests {
2029
2065
base_penalty_msat : 0 ,
2030
2066
liquidity_penalty_multiplier_msat : 1_000 ,
2031
2067
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2068
+ ..Default :: default ( )
2032
2069
} ;
2033
2070
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2034
2071
let source = source_node_id ( ) ;
@@ -2073,6 +2110,29 @@ mod tests {
2073
2110
assert_eq ! ( scorer. channel_penalty_msat( 42 , 128 , 1_024 , & source, & target) , 558 ) ;
2074
2111
}
2075
2112
2113
+ #[ test]
2114
+ fn adds_amount_penalty_to_liquidity_penalty ( ) {
2115
+ let network_graph = network_graph ( ) ;
2116
+ let source = source_node_id ( ) ;
2117
+ let target = target_node_id ( ) ;
2118
+
2119
+ let params = ProbabilisticScoringParameters {
2120
+ liquidity_penalty_multiplier_msat : 1_000 ,
2121
+ include_amount_penalty : false ,
2122
+ ..Default :: default ( )
2123
+ } ;
2124
+ let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2125
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512_000 , 1_024_000 , & source, & target) , 800 ) ;
2126
+
2127
+ let params = ProbabilisticScoringParameters {
2128
+ liquidity_penalty_multiplier_msat : 1_000 ,
2129
+ include_amount_penalty : true ,
2130
+ ..Default :: default ( )
2131
+ } ;
2132
+ let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2133
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512_000 , 1_024_000 , & source, & target) , 950 ) ;
2134
+ }
2135
+
2076
2136
#[ test]
2077
2137
fn calculates_log10_without_overflowing_u64_max_value ( ) {
2078
2138
let network_graph = network_graph ( ) ;
0 commit comments