@@ -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 (`-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,40 @@ 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
+ #[ inline( always) ]
737
+ fn combined_penalty_msat (
738
+ & self , amount_msat : u64 , negative_log10_times_1024 : u64 ,
739
+ params : ProbabilisticScoringParameters
740
+ ) -> u64 {
741
+ let liquidity_penalty_msat = {
742
+ // Upper bound the liquidity penalty to ensure some channel is selected.
743
+ let multiplier_msat = params. liquidity_penalty_multiplier_msat ;
744
+ let max_penalty_msat = multiplier_msat. saturating_mul ( NEGATIVE_LOG10_UPPER_BOUND ) ;
745
+ ( negative_log10_times_1024. saturating_mul ( multiplier_msat) / 1024 ) . min ( max_penalty_msat)
746
+ } ;
747
+ let amount_penalty_msat = negative_log10_times_1024
748
+ . saturating_mul ( params. amount_penalty_multiplier_msat )
749
+ . saturating_mul ( amount_msat) / 1024 / AMOUNT_PENALTY_DIVISOR ;
750
+
751
+ params. base_penalty_msat
752
+ . saturating_add ( liquidity_penalty_msat)
753
+ . saturating_add ( amount_penalty_msat)
754
+ }
755
+
696
756
/// Returns the lower bound of the channel liquidity balance in this direction.
697
757
fn min_liquidity_msat ( & self ) -> u64 {
698
758
self . decayed_offset_msat ( * self . min_liquidity_offset_msat )
@@ -762,14 +822,12 @@ impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsin
762
822
& self , short_channel_id : u64 , amount_msat : u64 , capacity_msat : u64 , source : & NodeId ,
763
823
target : & NodeId
764
824
) -> u64 {
765
- let liquidity_penalty_multiplier_msat = self . params . liquidity_penalty_multiplier_msat ;
766
825
let liquidity_offset_half_life = self . params . liquidity_offset_half_life ;
767
826
self . channel_liquidities
768
827
. get ( & short_channel_id)
769
828
. unwrap_or ( & ChannelLiquidity :: new ( ) )
770
829
. 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 )
830
+ . penalty_msat ( amount_msat, self . params )
773
831
}
774
832
775
833
fn payment_path_failed ( & mut self , path : & [ & RouteHop ] , short_channel_id : u64 ) {
@@ -1747,7 +1805,8 @@ mod tests {
1747
1805
fn increased_penalty_nearing_liquidity_upper_bound ( ) {
1748
1806
let network_graph = network_graph ( ) ;
1749
1807
let params = ProbabilisticScoringParameters {
1750
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1808
+ liquidity_penalty_multiplier_msat : 1_000 ,
1809
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1751
1810
} ;
1752
1811
let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1753
1812
let source = source_node_id ( ) ;
@@ -1772,7 +1831,8 @@ mod tests {
1772
1831
let last_updated = SinceEpoch :: now ( ) ;
1773
1832
let network_graph = network_graph ( ) ;
1774
1833
let params = ProbabilisticScoringParameters {
1775
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1834
+ liquidity_penalty_multiplier_msat : 1_000 ,
1835
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1776
1836
} ;
1777
1837
let scorer = ProbabilisticScorer :: new ( params, & network_graph)
1778
1838
. with_channel ( 42 ,
@@ -1792,7 +1852,8 @@ mod tests {
1792
1852
fn does_not_further_penalize_own_channel ( ) {
1793
1853
let network_graph = network_graph ( ) ;
1794
1854
let params = ProbabilisticScoringParameters {
1795
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1855
+ liquidity_penalty_multiplier_msat : 1_000 ,
1856
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1796
1857
} ;
1797
1858
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1798
1859
let sender = sender_node_id ( ) ;
@@ -1813,7 +1874,8 @@ mod tests {
1813
1874
fn sets_liquidity_lower_bound_on_downstream_failure ( ) {
1814
1875
let network_graph = network_graph ( ) ;
1815
1876
let params = ProbabilisticScoringParameters {
1816
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1877
+ liquidity_penalty_multiplier_msat : 1_000 ,
1878
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1817
1879
} ;
1818
1880
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1819
1881
let source = source_node_id ( ) ;
@@ -1835,7 +1897,8 @@ mod tests {
1835
1897
fn sets_liquidity_upper_bound_on_failure ( ) {
1836
1898
let network_graph = network_graph ( ) ;
1837
1899
let params = ProbabilisticScoringParameters {
1838
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1900
+ liquidity_penalty_multiplier_msat : 1_000 ,
1901
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1839
1902
} ;
1840
1903
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1841
1904
let source = source_node_id ( ) ;
@@ -1857,7 +1920,8 @@ mod tests {
1857
1920
fn reduces_liquidity_upper_bound_along_path_on_success ( ) {
1858
1921
let network_graph = network_graph ( ) ;
1859
1922
let params = ProbabilisticScoringParameters {
1860
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
1923
+ liquidity_penalty_multiplier_msat : 1_000 ,
1924
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1861
1925
} ;
1862
1926
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1863
1927
let sender = sender_node_id ( ) ;
@@ -1881,9 +1945,9 @@ mod tests {
1881
1945
fn decays_liquidity_bounds_over_time ( ) {
1882
1946
let network_graph = network_graph ( ) ;
1883
1947
let params = ProbabilisticScoringParameters {
1884
- base_penalty_msat : 0 ,
1885
1948
liquidity_penalty_multiplier_msat : 1_000 ,
1886
1949
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
1950
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1887
1951
} ;
1888
1952
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1889
1953
let source = source_node_id ( ) ;
@@ -1933,9 +1997,9 @@ mod tests {
1933
1997
fn decays_liquidity_bounds_without_shift_overflow ( ) {
1934
1998
let network_graph = network_graph ( ) ;
1935
1999
let params = ProbabilisticScoringParameters {
1936
- base_penalty_msat : 0 ,
1937
2000
liquidity_penalty_multiplier_msat : 1_000 ,
1938
2001
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2002
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1939
2003
} ;
1940
2004
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1941
2005
let source = source_node_id ( ) ;
@@ -1958,9 +2022,9 @@ mod tests {
1958
2022
fn restricts_liquidity_bounds_after_decay ( ) {
1959
2023
let network_graph = network_graph ( ) ;
1960
2024
let params = ProbabilisticScoringParameters {
1961
- base_penalty_msat : 0 ,
1962
2025
liquidity_penalty_multiplier_msat : 1_000 ,
1963
2026
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2027
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
1964
2028
} ;
1965
2029
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
1966
2030
let source = source_node_id ( ) ;
@@ -1996,9 +2060,9 @@ mod tests {
1996
2060
fn restores_persisted_liquidity_bounds ( ) {
1997
2061
let network_graph = network_graph ( ) ;
1998
2062
let params = ProbabilisticScoringParameters {
1999
- base_penalty_msat : 0 ,
2000
2063
liquidity_penalty_multiplier_msat : 1_000 ,
2001
2064
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2065
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2002
2066
} ;
2003
2067
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2004
2068
let source = source_node_id ( ) ;
@@ -2026,9 +2090,9 @@ mod tests {
2026
2090
fn decays_persisted_liquidity_bounds ( ) {
2027
2091
let network_graph = network_graph ( ) ;
2028
2092
let params = ProbabilisticScoringParameters {
2029
- base_penalty_msat : 0 ,
2030
2093
liquidity_penalty_multiplier_msat : 1_000 ,
2031
2094
liquidity_offset_half_life : Duration :: from_secs ( 10 ) ,
2095
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2032
2096
} ;
2033
2097
let mut scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2034
2098
let source = source_node_id ( ) ;
@@ -2061,7 +2125,8 @@ mod tests {
2061
2125
let target = target_node_id ( ) ;
2062
2126
2063
2127
let params = ProbabilisticScoringParameters {
2064
- base_penalty_msat : 0 , liquidity_penalty_multiplier_msat : 1_000 , ..Default :: default ( )
2128
+ liquidity_penalty_multiplier_msat : 1_000 ,
2129
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2065
2130
} ;
2066
2131
let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2067
2132
assert_eq ! ( scorer. channel_penalty_msat( 42 , 128 , 1_024 , & source, & target) , 58 ) ;
@@ -2073,14 +2138,38 @@ mod tests {
2073
2138
assert_eq ! ( scorer. channel_penalty_msat( 42 , 128 , 1_024 , & source, & target) , 558 ) ;
2074
2139
}
2075
2140
2141
+ #[ test]
2142
+ fn adds_amount_penalty_to_liquidity_penalty ( ) {
2143
+ let network_graph = network_graph ( ) ;
2144
+ let source = source_node_id ( ) ;
2145
+ let target = target_node_id ( ) ;
2146
+
2147
+ let params = ProbabilisticScoringParameters {
2148
+ liquidity_penalty_multiplier_msat : 1_000 ,
2149
+ amount_penalty_multiplier_msat : 0 ,
2150
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2151
+ } ;
2152
+ let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2153
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512_000 , 1_024_000 , & source, & target) , 300 ) ;
2154
+
2155
+ let params = ProbabilisticScoringParameters {
2156
+ liquidity_penalty_multiplier_msat : 1_000 ,
2157
+ amount_penalty_multiplier_msat : 256 ,
2158
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2159
+ } ;
2160
+ let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2161
+ assert_eq ! ( scorer. channel_penalty_msat( 42 , 512_000 , 1_024_000 , & source, & target) , 337 ) ;
2162
+ }
2163
+
2076
2164
#[ test]
2077
2165
fn calculates_log10_without_overflowing_u64_max_value ( ) {
2078
2166
let network_graph = network_graph ( ) ;
2079
2167
let source = source_node_id ( ) ;
2080
2168
let target = target_node_id ( ) ;
2081
2169
2082
2170
let params = ProbabilisticScoringParameters {
2083
- base_penalty_msat : 0 , ..Default :: default ( )
2171
+ liquidity_penalty_multiplier_msat : 40_000 ,
2172
+ ..ProbabilisticScoringParameters :: zero_penalty ( )
2084
2173
} ;
2085
2174
let scorer = ProbabilisticScorer :: new ( params, & network_graph) ;
2086
2175
assert_eq ! (
0 commit comments