@@ -832,7 +832,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
832832 /// [`Self::historical_estimated_channel_liquidity_probabilities`] (but not those returned by
833833 /// [`Self::estimated_channel_liquidity_range`]).
834834 pub fn historical_estimated_payment_success_probability (
835- & self , scid : u64 , target : & NodeId , amount_msat : u64 )
835+ & self , scid : u64 , target : & NodeId , amount_msat : u64 , params : & ProbabilisticScoringFeeParameters )
836836 -> Option < f64 > {
837837 let graph = self . network_graph . read_only ( ) ;
838838
@@ -844,7 +844,8 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
844844
845845 return dir_liq. liquidity_history . calculate_success_probability_times_billion (
846846 dir_liq. now , * dir_liq. last_updated ,
847- self . decay_params . historical_no_updates_half_life , amount_msat, capacity_msat
847+ self . decay_params . historical_no_updates_half_life , & params, amount_msat,
848+ capacity_msat
848849 ) . map ( |p| p as f64 / ( 1024 * 1024 * 1024 ) as f64 ) ;
849850 }
850851 }
@@ -936,6 +937,23 @@ const PRECISION_LOWER_BOUND_DENOMINATOR: u64 = approx::LOWER_BITS_BOUND;
936937const AMOUNT_PENALTY_DIVISOR : u64 = 1 << 20 ;
937938const BASE_AMOUNT_PENALTY_DIVISOR : u64 = 1 << 30 ;
938939
940+ #[ inline( always) ]
941+ /// Given liquidity bounds, calculates the success probability (in the form of a numerator and
942+ /// denominator) of an HTLC. This is a key assumption in our scoring models.
943+ ///
944+ /// Must not return a numerator or denominator greater than 2^31 for arguments less than 2^31.
945+
946+ /// min_zero_implies_no_successes signals that an `amount_msat` of 0 means we've not (recently)
947+ /// seen an HTLC successfully complete over this channel.
948+ fn success_probability (
949+ min_liquidity_msat : u64 , amount_msat : u64 , max_liquidity_msat : u64 , _capacity_msat : u64 ,
950+ _params : & ProbabilisticScoringFeeParameters , _min_zero_implies_no_successes : bool ,
951+ ) -> ( u64 , u64 ) {
952+ let numerator = max_liquidity_msat - amount_msat;
953+ let denominator = ( max_liquidity_msat - min_liquidity_msat) . saturating_add ( 1 ) ;
954+ ( numerator, denominator)
955+ }
956+
939957impl < L : Deref < Target = u64 > , BRT : Deref < Target = HistoricalBucketRangeTracker > , T : Time , U : Deref < Target = T > > DirectedChannelLiquidity < L , BRT , T , U > {
940958 /// Returns a liquidity penalty for routing the given HTLC `amount_msat` through the channel in
941959 /// this direction.
@@ -956,9 +974,9 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
956974 score_params. liquidity_penalty_amount_multiplier_msat )
957975 . saturating_add ( score_params. considered_impossible_penalty_msat )
958976 } else {
959- let numerator = ( max_liquidity_msat - amount_msat ) . saturating_add ( 1 ) ;
960- let denominator = ( max_liquidity_msat - min_liquidity_msat ) . saturating_add ( 1 ) ;
961- if amount_msat - min_liquidity_msat < denominator / PRECISION_LOWER_BOUND_DENOMINATOR {
977+ let ( numerator, denominator ) = success_probability ( min_liquidity_msat ,
978+ amount_msat , max_liquidity_msat, available_capacity , score_params , false ) ;
979+ if denominator - numerator < denominator / PRECISION_LOWER_BOUND_DENOMINATOR {
962980 // If the failure probability is < 1.5625% (as 1 - numerator/denominator < 1/64),
963981 // don't bother trying to use the log approximation as it gets too noisy to be
964982 // particularly helpful, instead just round down to 0.
@@ -985,7 +1003,8 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
9851003 score_params. historical_liquidity_penalty_amount_multiplier_msat != 0 {
9861004 if let Some ( cumulative_success_prob_times_billion) = self . liquidity_history
9871005 . calculate_success_probability_times_billion ( self . now , * self . last_updated ,
988- self . decay_params . historical_no_updates_half_life , amount_msat, self . capacity_msat )
1006+ self . decay_params . historical_no_updates_half_life , score_params, amount_msat,
1007+ self . capacity_msat )
9891008 {
9901009 let historical_negative_log10_times_2048 = approx:: negative_log10_times_2048 ( cumulative_success_prob_times_billion + 1 , 1024 * 1024 * 1024 ) ;
9911010 res = res. saturating_add ( Self :: combined_penalty_msat ( amount_msat,
@@ -995,9 +1014,8 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
9951014 // If we don't have any valid points (or, once decayed, we have less than a full
9961015 // point), redo the non-historical calculation with no liquidity bounds tracked and
9971016 // the historical penalty multipliers.
998- let available_capacity = self . available_capacity ( ) ;
999- let numerator = available_capacity. saturating_sub ( amount_msat) . saturating_add ( 1 ) ;
1000- let denominator = available_capacity. saturating_add ( 1 ) ;
1017+ let ( numerator, denominator) = success_probability ( 0 , amount_msat,
1018+ available_capacity, available_capacity, score_params, true ) ;
10011019 let negative_log10_times_2048 =
10021020 approx:: negative_log10_times_2048 ( numerator, denominator) ;
10031021 res = res. saturating_add ( Self :: combined_penalty_msat ( amount_msat, negative_log10_times_2048,
@@ -1777,8 +1795,9 @@ mod bucketed_history {
17771795
17781796 #[ inline]
17791797 pub ( super ) fn calculate_success_probability_times_billion < T : Time > (
1780- & self , now : T , last_updated : T , half_life : Duration , amount_msat : u64 , capacity_msat : u64 )
1781- -> Option < u64 > {
1798+ & self , now : T , last_updated : T , half_life : Duration ,
1799+ params : & ProbabilisticScoringFeeParameters , amount_msat : u64 , capacity_msat : u64
1800+ ) -> Option < u64 > {
17821801 // If historical penalties are enabled, calculate the penalty by walking the set of
17831802 // historical liquidity bucket (min, max) combinations (where min_idx < max_idx) and, for
17841803 // each, calculate the probability of success given our payment amount, then total the
@@ -1814,14 +1833,13 @@ mod bucketed_history {
18141833 }
18151834 let max_bucket_end_pos = BUCKET_START_POS [ 32 - highest_max_bucket_with_points] - 1 ;
18161835 if payment_pos < max_bucket_end_pos {
1836+ let ( numerator, denominator) = success_probability ( 0 , payment_pos as u64 ,
1837+ max_bucket_end_pos as u64 , MIN_SIZE_BUCKETS as u64 - 1 , params, true ) ;
18171838 let bucket_prob_times_billion =
18181839 ( self . min_liquidity_offset_history . buckets [ 0 ] as u64 ) * total_max_points
18191840 * 1024 * 1024 * 1024 / total_valid_points_tracked;
18201841 cumulative_success_prob_times_billion += bucket_prob_times_billion *
1821- ( ( max_bucket_end_pos - payment_pos) as u64 ) /
1822- // Add an additional one in the divisor as the payment bucket has been
1823- // rounded down.
1824- ( max_bucket_end_pos + 1 ) as u64 ;
1842+ numerator / denominator;
18251843 }
18261844 }
18271845
@@ -1839,11 +1857,11 @@ mod bucketed_history {
18391857 } else if payment_pos < min_bucket_start_pos {
18401858 cumulative_success_prob_times_billion += bucket_prob_times_billion;
18411859 } else {
1860+ let ( numerator, denominator) = success_probability (
1861+ min_bucket_start_pos as u64 , payment_pos as u64 ,
1862+ max_bucket_end_pos as u64 , MIN_SIZE_BUCKETS as u64 - 1 , params, true ) ;
18421863 cumulative_success_prob_times_billion += bucket_prob_times_billion *
1843- ( ( max_bucket_end_pos - payment_pos) as u64 ) /
1844- // Add an additional one in the divisor as the payment bucket has been
1845- // rounded down.
1846- ( ( max_bucket_end_pos - min_bucket_start_pos + 1 ) as u64 ) ;
1864+ numerator / denominator;
18471865 }
18481866 }
18491867 }
@@ -2846,7 +2864,7 @@ mod tests {
28462864 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 300 ) ;
28472865
28482866 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2849- assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 365 ) ;
2867+ assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 370 ) ;
28502868 }
28512869
28522870 #[ test]
@@ -3073,7 +3091,7 @@ mod tests {
30733091 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 47 ) ;
30743092 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
30753093 None ) ;
3076- assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 42 ) ,
3094+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 42 , & params ) ,
30773095 None ) ;
30783096
30793097 scorer. payment_path_failed ( & payment_path_for_amount ( 1 ) , 42 ) ;
@@ -3084,9 +3102,9 @@ mod tests {
30843102 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
30853103 Some ( ( [ 32 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ,
30863104 [ 0 , 0 , 0 , 0 , 0 , 0 , 32 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ) ) ;
3087- assert ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 )
3105+ assert ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 , & params )
30883106 . unwrap( ) > 0.35 ) ;
3089- assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 500 ) ,
3107+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 500 , & params ) ,
30903108 Some ( 0.0 ) ) ;
30913109
30923110 // Even after we tell the scorer we definitely have enough available liquidity, it will
@@ -3101,11 +3119,11 @@ mod tests {
31013119 // The exact success probability is a bit complicated and involves integer rounding, so we
31023120 // simply check bounds here.
31033121 let five_hundred_prob =
3104- scorer. historical_estimated_payment_success_probability ( 42 , & target, 500 ) . unwrap ( ) ;
3122+ scorer. historical_estimated_payment_success_probability ( 42 , & target, 500 , & params ) . unwrap ( ) ;
31053123 assert ! ( five_hundred_prob > 0.66 ) ;
31063124 assert ! ( five_hundred_prob < 0.68 ) ;
31073125 let one_prob =
3108- scorer. historical_estimated_payment_success_probability ( 42 , & target, 1 ) . unwrap ( ) ;
3126+ scorer. historical_estimated_payment_success_probability ( 42 , & target, 1 , & params ) . unwrap ( ) ;
31093127 assert ! ( one_prob < 1.0 ) ;
31103128 assert ! ( one_prob > 0.95 ) ;
31113129
@@ -3117,7 +3135,7 @@ mod tests {
31173135 // data entirely instead.
31183136 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
31193137 None ) ;
3120- assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 ) , None ) ;
3138+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 , & params ) , None ) ;
31213139
31223140 let mut usage = ChannelUsage {
31233141 amount_msat : 100 ,
@@ -3281,7 +3299,7 @@ mod tests {
32813299 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
32823300 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
32833301 None ) ;
3284- assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 42 ) ,
3302+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 42 , & params ) ,
32853303 None ) ;
32863304
32873305 // Fail to pay once, and then check the buckets and penalty.
@@ -3296,14 +3314,14 @@ mod tests {
32963314 Some ( ( [ 32 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ,
32973315 [ 0 , 32 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ) ) ;
32983316 // The success probability estimate itself should be zero.
3299- assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, amount_msat) ,
3317+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, amount_msat, & params ) ,
33003318 Some ( 0.0 ) ) ;
33013319
33023320 // Now test again with the amount in the bottom bucket.
33033321 amount_msat /= 2 ;
33043322 // The new amount is entirely within the only minimum bucket with score, so the probability
33053323 // we assign is 1/2.
3306- assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, amount_msat) ,
3324+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, amount_msat, & params ) ,
33073325 Some ( 0.5 ) ) ;
33083326
33093327 // ...but once we see a failure, we consider the payment to be substantially less likely,
@@ -3313,7 +3331,7 @@ mod tests {
33133331 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
33143332 Some ( ( [ 63 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ,
33153333 [ 32 , 31 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ) ) ;
3316- assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, amount_msat) ,
3334+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, amount_msat, & params ) ,
33173335 Some ( 0.0 ) ) ;
33183336 }
33193337}
0 commit comments