@@ -517,7 +517,7 @@ pub struct ProbabilisticScorerUsingTime<G: Deref<Target = NetworkGraph>, T: Time
517
517
518
518
/// Parameters for configuring [`ProbabilisticScorer`].
519
519
///
520
- /// Used to configure a base penalty and a liquidity penalty , the sum of which is the channel
520
+ /// Used to configure base, liquidity, and amount penalties , the sum of which composes the channel
521
521
/// penalty (i.e., the amount in msats willing to be paid to avoid routing through the channel).
522
522
#[ derive( Clone , Copy ) ]
523
523
pub struct ProbabilisticScoringParameters {
@@ -555,6 +555,25 @@ 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
+ /// A multiplier used in conjunction with a payment amount and the negative `log10` of the
560
+ /// channel's success probability for the payment to determine the amount penalty.
561
+ ///
562
+ /// The purpose of the amount penalty is to avoid having fees dominate the channel cost (i.e.,
563
+ /// fees plus penalty) for large payments. The penalty is computed as the product of this
564
+ /// multiplier and `2^20`ths of the payment amount, weighted by the negative `log10` of the
565
+ /// success probability.
566
+ ///
567
+ /// `-log10(success_probability) * amount_penalty_multiplier_msat * amount_msat / 2^20`
568
+ ///
569
+ /// In practice, this means for 0.1 success probability (i.e., `-log10 == 1`) each `2^20`th of
570
+ /// the amount will result in a penalty of the multiplier. And, as the success probability
571
+ /// decreases, the negative `log10` weighting will increase dramatically. For higher success
572
+ /// probabilities, the multiplier will have a decreasing effect as the negative `log10` will
573
+ /// fall below `1`.
574
+ ///
575
+ /// Default value: 0
576
+ pub amount_penalty_multiplier_msat : u64 ,
558
577
}
559
578
560
579
/// Accounting for channel liquidity balance uncertainty.
@@ -608,6 +627,7 @@ impl Default for ProbabilisticScoringParameters {
608
627
base_penalty_msat : 500 ,
609
628
liquidity_penalty_multiplier_msat : 40_000 ,
610
629
liquidity_offset_half_life : Duration :: from_secs ( 3600 ) ,
630
+ amount_penalty_multiplier_msat : 0 ,
611
631
}
612
632
}
613
633
}
@@ -665,11 +685,17 @@ impl<T: Time> ChannelLiquidity<T> {
665
685
}
666
686
}
667
687
688
+ /// Bounds `-log10` to avoid excessive liquidity penalties for payments with low success
689
+ /// probabilities.
690
+ const NEGATIVE_LOG10_UPPER_BOUND : u64 = 2 ;
691
+
692
+ /// The divisor used when computing the amount penalty.
693
+ const AMOUNT_PENALTY_DIVISOR : u64 = 1 << 20 ;
694
+
668
695
impl < L : Deref < Target = u64 > , T : Time , U : Deref < Target = T > > DirectedChannelLiquidity < L , T , U > {
669
696
/// Returns a penalty for routing the given HTLC `amount_msat` through the channel in this
670
697
/// 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 ) ;
698
+ fn penalty_msat ( & self , amount_msat : u64 , params : ProbabilisticScoringParameters ) -> u64 {
673
699
let max_liquidity_msat = self . max_liquidity_msat ( ) ;
674
700
let min_liquidity_msat = core:: cmp:: min ( self . min_liquidity_msat ( ) , max_liquidity_msat) ;
675
701
if amount_msat <= min_liquidity_msat {
@@ -681,18 +707,40 @@ impl<L: Deref<Target = u64>, T: Time, U: Deref<Target = T>> DirectedChannelLiqui
681
707
// Avoid using the failed channel on retry.
682
708
u64:: max_value ( )
683
709
} else {
684
- max_penalty_msat
710
+ // Equivalent to hitting the else clause below with the amount equal to the
711
+ // effective capacity and without any certainty on the liquidity upper bound.
712
+ let negative_log10_times_1024 = NEGATIVE_LOG10_UPPER_BOUND * 1024 ;
713
+ self . combined_penalty_msat ( amount_msat, negative_log10_times_1024, params)
685
714
}
686
715
} else {
687
716
let numerator = ( max_liquidity_msat - amount_msat) . saturating_add ( 1 ) ;
688
717
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)
718
+ let negative_log10_times_1024 =
719
+ approx:: negative_log10_times_1024 ( numerator, denominator) ;
720
+ self . combined_penalty_msat ( amount_msat, negative_log10_times_1024, params)
693
721
}
694
722
}
695
723
724
+ #[ inline( always) ]
725
+ fn combined_penalty_msat (
726
+ & self , amount_msat : u64 , negative_log10_times_1024 : u64 ,
727
+ params : ProbabilisticScoringParameters
728
+ ) -> u64 {
729
+ let liquidity_penalty_msat = {
730
+ // Upper bound the liquidity penalty to ensure some channel is selected.
731
+ let multiplier_msat = params. liquidity_penalty_multiplier_msat ;
732
+ let max_penalty_msat = multiplier_msat. saturating_mul ( NEGATIVE_LOG10_UPPER_BOUND ) ;
733
+ ( negative_log10_times_1024. saturating_mul ( multiplier_msat) / 1024 ) . min ( max_penalty_msat)
734
+ } ;
735
+ let amount_penalty_msat = negative_log10_times_1024
736
+ . saturating_mul ( params. amount_penalty_multiplier_msat )
737
+ . saturating_mul ( amount_msat) / 1024 / AMOUNT_PENALTY_DIVISOR ;
738
+
739
+ params. base_penalty_msat
740
+ . saturating_add ( liquidity_penalty_msat)
741
+ . saturating_add ( amount_penalty_msat)
742
+ }
743
+
696
744
/// Returns the lower bound of the channel liquidity balance in this direction.
697
745
fn min_liquidity_msat ( & self ) -> u64 {
698
746
self . decayed_offset_msat ( * self . min_liquidity_offset_msat )
@@ -762,14 +810,12 @@ impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsin
762
810
& self , short_channel_id : u64 , amount_msat : u64 , capacity_msat : u64 , source : & NodeId ,
763
811
target : & NodeId
764
812
) -> u64 {
765
- let liquidity_penalty_multiplier_msat = self . params . liquidity_penalty_multiplier_msat ;
766
813
let liquidity_offset_half_life = self . params . liquidity_offset_half_life ;
767
814
self . channel_liquidities
768
815
. get ( & short_channel_id)
769
816
. unwrap_or ( & ChannelLiquidity :: new ( ) )
770
817
. 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 )
818
+ . penalty_msat ( amount_msat, self . params )
773
819
}
774
820
775
821
fn payment_path_failed ( & mut self , path : & [ & RouteHop ] , short_channel_id : u64 ) {
@@ -1884,6 +1930,7 @@ mod tests {
1884
1930
base_penalty_msat : 0 ,
1885
1931
liquidity_penalty_multiplier_msat : 1_000 ,
1886
1932
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
1933
+ ..Default :: default ( )
1887
1934
} ;
1888
1935
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1889
1936
let source = source_node_id ( ) ;
@@ -1936,6 +1983,7 @@ mod tests {
1936
1983
base_penalty_msat : 0 ,
1937
1984
liquidity_penalty_multiplier_msat : 1_000 ,
1938
1985
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
1986
+ ..Default :: default ( )
1939
1987
} ;
1940
1988
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1941
1989
let source = source_node_id ( ) ;
@@ -1961,6 +2009,7 @@ mod tests {
1961
2009
base_penalty_msat : 0 ,
1962
2010
liquidity_penalty_multiplier_msat : 1_000 ,
1963
2011
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2012
+ ..Default :: default ( )
1964
2013
} ;
1965
2014
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1966
2015
let source = source_node_id ( ) ;
@@ -1999,6 +2048,7 @@ mod tests {
1999
2048
base_penalty_msat : 0 ,
2000
2049
liquidity_penalty_multiplier_msat : 1_000 ,
2001
2050
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2051
+ ..Default :: default ( )
2002
2052
} ;
2003
2053
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2004
2054
let source = source_node_id ( ) ;
@@ -2029,6 +2079,7 @@ mod tests {
2029
2079
base_penalty_msat : 0 ,
2030
2080
liquidity_penalty_multiplier_msat : 1_000 ,
2031
2081
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2082
+ ..Default :: default ( )
2032
2083
} ;
2033
2084
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2034
2085
let source = source_node_id ( ) ;
@@ -2073,6 +2124,29 @@ mod tests {
2073
2124
assert_eq ! ( scorer. channel_penalty_msat( 42 , 128 , 1_024 , & source, & target) , 558 ) ;
2074
2125
}
2075
2126
2127
+ #[ test]
2128
+ fn adds_amount_penalty_to_liquidity_penalty ( ) {
2129
+ let network_graph = network_graph ( ) ;
2130
+ let source = source_node_id ( ) ;
2131
+ let target = target_node_id ( ) ;
2132
+
2133
+ let params = ProbabilisticScoringParameters {
2134
+ liquidity_penalty_multiplier_msat : 1_000 ,
2135
+ amount_penalty_multiplier_msat : 0 ,
2136
+ ..Default :: default ( )
2137
+ } ;
2138
+ let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2139
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512_000 , 1_024_000 , & source, & target) , 800 ) ;
2140
+
2141
+ let params = ProbabilisticScoringParameters {
2142
+ liquidity_penalty_multiplier_msat : 1_000 ,
2143
+ amount_penalty_multiplier_msat : 256 ,
2144
+ ..Default :: default ( )
2145
+ } ;
2146
+ let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2147
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512_000 , 1_024_000 , & source, & target) , 837 ) ;
2148
+ }
2149
+
2076
2150
#[ test]
2077
2151
fn calculates_log10_without_overflowing_u64_max_value ( ) {
2078
2152
let network_graph = network_graph ( ) ;
0 commit comments