@@ -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 comprises 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 (`-log10(0.1) == 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: 256 msat
576
+ pub amount_penalty_multiplier_msat : u64 ,
558
577
}
559
578
560
579
/// Accounting for channel liquidity balance uncertainty.
@@ -602,12 +621,25 @@ impl<G: Deref<Target = NetworkGraph>, T: Time> ProbabilisticScorerUsingTime<G, T
602
621
}
603
622
}
604
623
624
+ impl ProbabilisticScoringParameters {
625
+ #[ cfg( test) ]
626
+ fn zero_penalty ( ) -> Self {
627
+ Self {
628
+ base_penalty_msat : 0 ,
629
+ liquidity_penalty_multiplier_msat : 0 ,
630
+ liquidity_offset_half_life : Duration :: from_secs ( 3600 ) ,
631
+ amount_penalty_multiplier_msat : 0 ,
632
+ }
633
+ }
634
+ }
635
+
605
636
impl Default for ProbabilisticScoringParameters {
606
637
fn default ( ) -> Self {
607
638
Self {
608
639
base_penalty_msat : 500 ,
609
640
liquidity_penalty_multiplier_msat : 40_000 ,
610
641
liquidity_offset_half_life : Duration :: from_secs ( 3600 ) ,
642
+ amount_penalty_multiplier_msat : 256 ,
611
643
}
612
644
}
613
645
}
@@ -665,11 +697,17 @@ impl<T: Time> ChannelLiquidity<T> {
665
697
}
666
698
}
667
699
700
+ /// Bounds `-log10` to avoid excessive liquidity penalties for payments with low success
701
+ /// probabilities.
702
+ const NEGATIVE_LOG10_UPPER_BOUND : u64 = 2 ;
703
+
704
+ /// The divisor used when computing the amount penalty.
705
+ const AMOUNT_PENALTY_DIVISOR : u64 = 1 << 20 ;
706
+
668
707
impl < L : Deref < Target = u64 > , T : Time , U : Deref < Target = T > > DirectedChannelLiquidity < L , T , U > {
669
708
/// Returns a penalty for routing the given HTLC `amount_msat` through the channel in this
670
709
/// 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 ) ;
710
+ fn penalty_msat ( & self , amount_msat : u64 , params : ProbabilisticScoringParameters ) -> u64 {
673
711
let max_liquidity_msat = self . max_liquidity_msat ( ) ;
674
712
let min_liquidity_msat = core:: cmp:: min ( self . min_liquidity_msat ( ) , max_liquidity_msat) ;
675
713
if amount_msat <= min_liquidity_msat {
@@ -681,18 +719,41 @@ impl<L: Deref<Target = u64>, T: Time, U: Deref<Target = T>> DirectedChannelLiqui
681
719
// Avoid using the failed channel on retry.
682
720
u64:: max_value ( )
683
721
} else {
684
- max_penalty_msat
722
+ // Equivalent to hitting the else clause below with the amount equal to the
723
+ // effective capacity and without any certainty on the liquidity upper bound.
724
+ let negative_log10_times_1024 = NEGATIVE_LOG10_UPPER_BOUND * 1024 ;
725
+ self . combined_penalty_msat ( amount_msat, negative_log10_times_1024, params)
685
726
}
686
727
} else {
687
728
let numerator = ( max_liquidity_msat - amount_msat) . saturating_add ( 1 ) ;
688
729
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)
730
+ let negative_log10_times_1024 =
731
+ approx:: negative_log10_times_1024 ( numerator, denominator) ;
732
+ self . combined_penalty_msat ( amount_msat, negative_log10_times_1024, params)
693
733
}
694
734
}
695
735
736
+ /// Computes the liquidity and amount penalties and adds them to the base penalty.
737
+ #[ inline( always) ]
738
+ fn combined_penalty_msat (
739
+ & self , amount_msat : u64 , negative_log10_times_1024 : u64 ,
740
+ params : ProbabilisticScoringParameters
741
+ ) -> u64 {
742
+ let liquidity_penalty_msat = {
743
+ // Upper bound the liquidity penalty to ensure some channel is selected.
744
+ let multiplier_msat = params. liquidity_penalty_multiplier_msat ;
745
+ let max_penalty_msat = multiplier_msat. saturating_mul ( NEGATIVE_LOG10_UPPER_BOUND ) ;
746
+ ( negative_log10_times_1024. saturating_mul ( multiplier_msat) / 1024 ) . min ( max_penalty_msat)
747
+ } ;
748
+ let amount_penalty_msat = negative_log10_times_1024
749
+ . saturating_mul ( params. amount_penalty_multiplier_msat )
750
+ . saturating_mul ( amount_msat) / 1024 / AMOUNT_PENALTY_DIVISOR ;
751
+
752
+ params. base_penalty_msat
753
+ . saturating_add ( liquidity_penalty_msat)
754
+ . saturating_add ( amount_penalty_msat)
755
+ }
756
+
696
757
/// Returns the lower bound of the channel liquidity balance in this direction.
697
758
fn min_liquidity_msat ( & self ) -> u64 {
698
759
self . decayed_offset_msat ( * self . min_liquidity_offset_msat )
@@ -762,14 +823,12 @@ impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsin
762
823
& self , short_channel_id : u64 , amount_msat : u64 , capacity_msat : u64 , source : & NodeId ,
763
824
target : & NodeId
764
825
) -> u64 {
765
- let liquidity_penalty_multiplier_msat = self . params . liquidity_penalty_multiplier_msat ;
766
826
let liquidity_offset_half_life = self . params . liquidity_offset_half_life ;
767
827
self . channel_liquidities
768
828
. get ( & short_channel_id)
769
829
. unwrap_or ( & ChannelLiquidity :: new ( ) )
770
830
. 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 )
831
+ . penalty_msat ( amount_msat, self . params )
773
832
}
774
833
775
834
fn payment_path_failed ( & mut self , path : & [ & RouteHop ] , short_channel_id : u64 ) {
@@ -1747,7 +1806,8 @@ mod tests {
1747
1806
fn increased_penalty_nearing_liquidity_upper_bound ( ) {
1748
1807
let network_graph = network_graph ( ) ;
1749
1808
let params = ProbabilisticScoringParameters {
1750
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1809
+ liquidity_penalty_multiplier_msat : 1_000 ,
1810
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1751
1811
} ;
1752
1812
let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1753
1813
let source = source_node_id ( ) ;
@@ -1772,7 +1832,8 @@ mod tests {
1772
1832
let last_updated = SinceEpoch :: now ( ) ;
1773
1833
let network_graph = network_graph ( ) ;
1774
1834
let params = ProbabilisticScoringParameters {
1775
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1835
+ liquidity_penalty_multiplier_msat : 1_000 ,
1836
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1776
1837
} ;
1777
1838
let scorer = ProbabilisticScorer :: new ( params, & network_graph)
1778
1839
. with_channel ( 42 ,
@@ -1792,7 +1853,8 @@ mod tests {
1792
1853
fn does_not_further_penalize_own_channel ( ) {
1793
1854
let network_graph = network_graph ( ) ;
1794
1855
let params = ProbabilisticScoringParameters {
1795
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1856
+ liquidity_penalty_multiplier_msat : 1_000 ,
1857
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1796
1858
} ;
1797
1859
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1798
1860
let sender = sender_node_id ( ) ;
@@ -1813,7 +1875,8 @@ mod tests {
1813
1875
fn sets_liquidity_lower_bound_on_downstream_failure ( ) {
1814
1876
let network_graph = network_graph ( ) ;
1815
1877
let params = ProbabilisticScoringParameters {
1816
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1878
+ liquidity_penalty_multiplier_msat : 1_000 ,
1879
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1817
1880
} ;
1818
1881
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1819
1882
let source = source_node_id ( ) ;
@@ -1835,7 +1898,8 @@ mod tests {
1835
1898
fn sets_liquidity_upper_bound_on_failure ( ) {
1836
1899
let network_graph = network_graph ( ) ;
1837
1900
let params = ProbabilisticScoringParameters {
1838
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1901
+ liquidity_penalty_multiplier_msat : 1_000 ,
1902
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1839
1903
} ;
1840
1904
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1841
1905
let source = source_node_id ( ) ;
@@ -1857,7 +1921,8 @@ mod tests {
1857
1921
fn reduces_liquidity_upper_bound_along_path_on_success ( ) {
1858
1922
let network_graph = network_graph ( ) ;
1859
1923
let params = ProbabilisticScoringParameters {
1860
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1924
+ liquidity_penalty_multiplier_msat : 1_000 ,
1925
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1861
1926
} ;
1862
1927
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1863
1928
let sender = sender_node_id ( ) ;
@@ -1881,9 +1946,9 @@ mod tests {
1881
1946
fn decays_liquidity_bounds_over_time ( ) {
1882
1947
let network_graph = network_graph ( ) ;
1883
1948
let params = ProbabilisticScoringParameters {
1884
- base_penalty_msat : 0 ,
1885
1949
liquidity_penalty_multiplier_msat : 1_000 ,
1886
1950
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
1951
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1887
1952
} ;
1888
1953
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1889
1954
let source = source_node_id ( ) ;
@@ -1933,9 +1998,9 @@ mod tests {
1933
1998
fn decays_liquidity_bounds_without_shift_overflow ( ) {
1934
1999
let network_graph = network_graph ( ) ;
1935
2000
let params = ProbabilisticScoringParameters {
1936
- base_penalty_msat : 0 ,
1937
2001
liquidity_penalty_multiplier_msat : 1_000 ,
1938
2002
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2003
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1939
2004
} ;
1940
2005
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1941
2006
let source = source_node_id ( ) ;
@@ -1958,9 +2023,9 @@ mod tests {
1958
2023
fn restricts_liquidity_bounds_after_decay ( ) {
1959
2024
let network_graph = network_graph ( ) ;
1960
2025
let params = ProbabilisticScoringParameters {
1961
- base_penalty_msat : 0 ,
1962
2026
liquidity_penalty_multiplier_msat : 1_000 ,
1963
2027
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2028
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1964
2029
} ;
1965
2030
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1966
2031
let source = source_node_id ( ) ;
@@ -1996,9 +2061,9 @@ mod tests {
1996
2061
fn restores_persisted_liquidity_bounds ( ) {
1997
2062
let network_graph = network_graph ( ) ;
1998
2063
let params = ProbabilisticScoringParameters {
1999
- base_penalty_msat : 0 ,
2000
2064
liquidity_penalty_multiplier_msat : 1_000 ,
2001
2065
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2066
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2002
2067
} ;
2003
2068
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2004
2069
let source = source_node_id ( ) ;
@@ -2026,9 +2091,9 @@ mod tests {
2026
2091
fn decays_persisted_liquidity_bounds ( ) {
2027
2092
let network_graph = network_graph ( ) ;
2028
2093
let params = ProbabilisticScoringParameters {
2029
- base_penalty_msat : 0 ,
2030
2094
liquidity_penalty_multiplier_msat : 1_000 ,
2031
2095
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2096
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2032
2097
} ;
2033
2098
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2034
2099
let source = source_node_id ( ) ;
@@ -2061,7 +2126,8 @@ mod tests {
2061
2126
let target = target_node_id ( ) ;
2062
2127
2063
2128
let params = ProbabilisticScoringParameters {
2064
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
2129
+ liquidity_penalty_multiplier_msat : 1_000 ,
2130
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2065
2131
} ;
2066
2132
let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2067
2133
assert_eq ! ( scorer. channel_penalty_msat( 42 , 128 , 1_024 , & source, & target) , 58 ) ;
@@ -2073,14 +2139,38 @@ mod tests {
2073
2139
assert_eq ! ( scorer. channel_penalty_msat( 42 , 128 , 1_024 , & source, & target) , 558 ) ;
2074
2140
}
2075
2141
2142
+ #[ test]
2143
+ fn adds_amount_penalty_to_liquidity_penalty ( ) {
2144
+ let network_graph = network_graph ( ) ;
2145
+ let source = source_node_id ( ) ;
2146
+ let target = target_node_id ( ) ;
2147
+
2148
+ let params = ProbabilisticScoringParameters {
2149
+ liquidity_penalty_multiplier_msat : 1_000 ,
2150
+ amount_penalty_multiplier_msat : 0 ,
2151
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2152
+ } ;
2153
+ let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2154
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512_000 , 1_024_000 , & source, & target) , 300 ) ;
2155
+
2156
+ let params = ProbabilisticScoringParameters {
2157
+ liquidity_penalty_multiplier_msat : 1_000 ,
2158
+ amount_penalty_multiplier_msat : 256 ,
2159
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2160
+ } ;
2161
+ let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2162
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512_000 , 1_024_000 , & source, & target) , 337 ) ;
2163
+ }
2164
+
2076
2165
#[ test]
2077
2166
fn calculates_log10_without_overflowing_u64_max_value ( ) {
2078
2167
let network_graph = network_graph ( ) ;
2079
2168
let source = source_node_id ( ) ;
2080
2169
let target = target_node_id ( ) ;
2081
2170
2082
2171
let params = ProbabilisticScoringParameters {
2083
- base_penalty_msat : 0 , ..Default :: default ( )
2172
+ liquidity_penalty_multiplier_msat : 40_000 ,
2173
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2084
2174
} ;
2085
2175
let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2086
2176
assert_eq ! (
0 commit comments