@@ -851,8 +851,6 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
851851 /// Note that this writes roughly one line per channel for which we have a liquidity estimate,
852852 /// which may be a substantial amount of log output.
853853 pub fn debug_log_liquidity_stats ( & self ) {
854- let now = T :: now ( ) ;
855-
856854 let graph = self . network_graph . read_only ( ) ;
857855 for ( scid, liq) in self . channel_liquidities . iter ( ) {
858856 if let Some ( chan_debug) = graph. channels ( ) . get ( scid) {
@@ -861,10 +859,8 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
861859 let amt = directed_info. effective_capacity ( ) . as_msat ( ) ;
862860 let dir_liq = liq. as_directed ( source, target, amt, self . decay_params ) ;
863861
864- let ( min_buckets, max_buckets) = dir_liq. liquidity_history
865- . get_decayed_buckets ( now, * dir_liq. offset_history_last_updated ,
866- self . decay_params . historical_no_updates_half_life )
867- . unwrap_or ( ( [ 0 ; 32 ] , [ 0 ; 32 ] ) ) ;
862+ let min_buckets = & dir_liq. liquidity_history . min_liquidity_offset_history . buckets ;
863+ let max_buckets = & dir_liq. liquidity_history . max_liquidity_offset_history . buckets ;
868864
869865 log_debug ! ( self . logger, core:: concat!(
870866 "Liquidity from {} to {} via {} is in the range ({}, {}).\n " ,
@@ -943,7 +939,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
943939 /// in the top and bottom bucket, and roughly with similar (recent) frequency.
944940 ///
945941 /// Because the datapoints are decayed slowly over time, values will eventually return to
946- /// `Some(([1 ; 32], [1 ; 32]))` and then to `None` once no datapoints remain .
942+ /// `Some(([0 ; 32], [0 ; 32]))` or `None` if no data remains for a channel .
947943 ///
948944 /// In order to fetch a single success probability from the buckets provided here, as used in
949945 /// the scoring model, see [`Self::historical_estimated_payment_success_probability`].
@@ -957,11 +953,8 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
957953 let amt = directed_info. effective_capacity ( ) . as_msat ( ) ;
958954 let dir_liq = liq. as_directed ( source, target, amt, self . decay_params ) ;
959955
960- let ( min_buckets, mut max_buckets) =
961- dir_liq. liquidity_history . get_decayed_buckets (
962- dir_liq. now , * dir_liq. offset_history_last_updated ,
963- self . decay_params . historical_no_updates_half_life
964- ) ?;
956+ let min_buckets = dir_liq. liquidity_history . min_liquidity_offset_history . buckets ;
957+ let mut max_buckets = dir_liq. liquidity_history . max_liquidity_offset_history . buckets ;
965958
966959 // Note that the liquidity buckets are an offset from the edge, so we inverse
967960 // the max order to get the probabilities from zero.
@@ -992,9 +985,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
992985 let dir_liq = liq. as_directed ( source, target, capacity_msat, self . decay_params ) ;
993986
994987 return dir_liq. liquidity_history . calculate_success_probability_times_billion (
995- dir_liq. now , * dir_liq. offset_history_last_updated ,
996- self . decay_params . historical_no_updates_half_life , & params, amount_msat,
997- capacity_msat
988+ & params, amount_msat, capacity_msat
998989 ) . map ( |p| p as f64 / ( 1024 * 1024 * 1024 ) as f64 ) ;
999990 }
1000991 }
@@ -1215,9 +1206,7 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
12151206 score_params. historical_liquidity_penalty_amount_multiplier_msat != 0 {
12161207 if let Some ( cumulative_success_prob_times_billion) = self . liquidity_history
12171208 . calculate_success_probability_times_billion (
1218- self . now , * self . offset_history_last_updated ,
1219- self . decay_params . historical_no_updates_half_life , score_params, amount_msat,
1220- self . capacity_msat )
1209+ score_params, amount_msat, self . capacity_msat )
12211210 {
12221211 let historical_negative_log10_times_2048 = approx:: negative_log10_times_2048 ( cumulative_success_prob_times_billion + 1 , 1024 * 1024 * 1024 ) ;
12231212 res = res. saturating_add ( Self :: combined_penalty_msat ( amount_msat,
@@ -2009,22 +1998,20 @@ mod bucketed_history {
20091998 }
20101999
20112000 impl < D : Deref < Target = HistoricalBucketRangeTracker > > HistoricalMinMaxBuckets < D > {
2012- pub ( super ) fn get_decayed_buckets < T : Time > ( & self , now : T , offset_history_last_updated : T , half_life : Duration )
2013- -> Option < ( [ u16 ; 32 ] , [ u16 ; 32 ] ) > {
2014- let ( _, required_decays) = self . get_total_valid_points ( now, offset_history_last_updated, half_life) ?;
2015-
2016- let mut min_buckets = * self . min_liquidity_offset_history ;
2017- min_buckets. time_decay_data ( required_decays) ;
2018- let mut max_buckets = * self . max_liquidity_offset_history ;
2019- max_buckets. time_decay_data ( required_decays) ;
2020- Some ( ( min_buckets. buckets , max_buckets. buckets ) )
2021- }
20222001 #[ inline]
2023- pub ( super ) fn get_total_valid_points < T : Time > ( & self , now : T , offset_history_last_updated : T , half_life : Duration )
2024- -> Option < ( u64 , u32 ) > {
2025- let required_decays = now. duration_since ( offset_history_last_updated) . as_secs ( )
2026- . checked_div ( half_life. as_secs ( ) )
2027- . map_or ( u32:: max_value ( ) , |decays| cmp:: min ( decays, u32:: max_value ( ) as u64 ) as u32 ) ;
2002+ pub ( super ) fn calculate_success_probability_times_billion (
2003+ & self , params : & ProbabilisticScoringFeeParameters , amount_msat : u64 ,
2004+ capacity_msat : u64
2005+ ) -> Option < u64 > {
2006+ // If historical penalties are enabled, we try to calculate a probability of success
2007+ // given our historical distribution of min- and max-liquidity bounds in a channel.
2008+ // To do so, we walk the set of historical liquidity bucket (min, max) combinations
2009+ // (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2010+ // state). For each pair, we calculate the probability as if the bucket's corresponding
2011+ // min- and max- liquidity bounds were our current liquidity bounds and then multiply
2012+ // that probability by the weight of the selected buckets.
2013+ let payment_pos = amount_to_pos ( amount_msat, capacity_msat) ;
2014+ if payment_pos >= POSITION_TICKS { return None ; }
20282015
20292016 let mut total_valid_points_tracked = 0 ;
20302017 for ( min_idx, min_bucket) in self . min_liquidity_offset_history . buckets . iter ( ) . enumerate ( ) {
@@ -2036,33 +2023,10 @@ mod bucketed_history {
20362023 // If the total valid points is smaller than 1.0 (i.e. 32 in our fixed-point scheme),
20372024 // treat it as if we were fully decayed.
20382025 const FULLY_DECAYED : u16 = BUCKET_FIXED_POINT_ONE * BUCKET_FIXED_POINT_ONE ;
2039- if total_valid_points_tracked. checked_shr ( required_decays ) . unwrap_or ( 0 ) < FULLY_DECAYED . into ( ) {
2026+ if total_valid_points_tracked < FULLY_DECAYED . into ( ) {
20402027 return None ;
20412028 }
20422029
2043- Some ( ( total_valid_points_tracked, required_decays) )
2044- }
2045-
2046- #[ inline]
2047- pub ( super ) fn calculate_success_probability_times_billion < T : Time > (
2048- & self , now : T , offset_history_last_updated : T , half_life : Duration ,
2049- params : & ProbabilisticScoringFeeParameters , amount_msat : u64 , capacity_msat : u64
2050- ) -> Option < u64 > {
2051- // If historical penalties are enabled, we try to calculate a probability of success
2052- // given our historical distribution of min- and max-liquidity bounds in a channel.
2053- // To do so, we walk the set of historical liquidity bucket (min, max) combinations
2054- // (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2055- // state). For each pair, we calculate the probability as if the bucket's corresponding
2056- // min- and max- liquidity bounds were our current liquidity bounds and then multiply
2057- // that probability by the weight of the selected buckets.
2058- let payment_pos = amount_to_pos ( amount_msat, capacity_msat) ;
2059- if payment_pos >= POSITION_TICKS { return None ; }
2060-
2061- // Check if all our buckets are zero, once decayed and treat it as if we had no data. We
2062- // don't actually use the decayed buckets, though, as that would lose precision.
2063- let ( total_valid_points_tracked, _)
2064- = self . get_total_valid_points ( now, offset_history_last_updated, half_life) ?;
2065-
20662030 let mut cumulative_success_prob_times_billion = 0 ;
20672031 // Special-case the 0th min bucket - it generally means we failed a payment, so only
20682032 // consider the highest (i.e. largest-offset-from-max-capacity) max bucket for all
@@ -2918,19 +2882,9 @@ mod tests {
29182882 let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
29192883 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
29202884
2921- // No decay
2922- SinceEpoch :: advance ( Duration :: from_secs ( 4 ) ) ;
2923- let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
2924- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
2925- let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
2926- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 93 ) ;
2927- let usage = ChannelUsage { amount_msat : 768 , ..usage } ;
2928- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 1_479 ) ;
2929- let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
2930- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
2931-
29322885 // Half decay (i.e., three-quarter life)
2933- SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
2886+ SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2887+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 5 ) ) ;
29342888 let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
29352889 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 22 ) ;
29362890 let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
@@ -2942,6 +2896,7 @@ mod tests {
29422896
29432897 // One decay (i.e., half life)
29442898 SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2899+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
29452900 let usage = ChannelUsage { amount_msat : 64 , ..usage } ;
29462901 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29472902 let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
@@ -2953,6 +2908,7 @@ mod tests {
29532908
29542909 // Fully decay liquidity lower bound.
29552910 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 7 ) ) ;
2911+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 8 ) ) ;
29562912 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29572913 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29582914 let usage = ChannelUsage { amount_msat : 1 , ..usage } ;
@@ -2964,12 +2920,14 @@ mod tests {
29642920
29652921 // Fully decay liquidity upper bound.
29662922 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2923+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 9 ) ) ;
29672924 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29682925 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29692926 let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
29702927 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
29712928
29722929 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2930+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 10 ) ) ;
29732931 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29742932 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29752933 let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
@@ -3004,9 +2962,11 @@ mod tests {
30042962 // An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
30052963 // would cause an overflow.
30062964 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 64 ) ) ;
2965+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 64 ) ) ;
30072966 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 125 ) ;
30082967
30092968 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2969+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 65 ) ) ;
30102970 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 125 ) ;
30112971 }
30122972
@@ -3040,6 +3000,7 @@ mod tests {
30403000
30413001 // Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
30423002 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3003+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
30433004 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 291 ) ;
30443005
30453006 // Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
@@ -3054,6 +3015,7 @@ mod tests {
30543015
30553016 // Further decaying affects the lower bound more than the upper bound (128, 928).
30563017 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3018+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 20 ) ) ;
30573019 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 280 ) ;
30583020 }
30593021
@@ -3083,6 +3045,7 @@ mod tests {
30833045 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
30843046
30853047 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3048+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
30863049 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 473 ) ;
30873050
30883051 scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
@@ -3097,8 +3060,7 @@ mod tests {
30973060 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 300 ) ;
30983061 }
30993062
3100- #[ test]
3101- fn decays_persisted_liquidity_bounds ( ) {
3063+ fn do_decays_persisted_liquidity_bounds ( decay_before_reload : bool ) {
31023064 let logger = TestLogger :: new ( ) ;
31033065 let network_graph = network_graph ( & logger) ;
31043066 let params = ProbabilisticScoringFeeParameters {
@@ -3122,23 +3084,38 @@ mod tests {
31223084 scorer. payment_path_failed ( & payment_path_for_amount ( 500 ) , 42 , Duration :: ZERO ) ;
31233085 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
31243086
3087+ if decay_before_reload {
3088+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3089+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3090+ }
3091+
31253092 let mut serialized_scorer = Vec :: new ( ) ;
31263093 scorer. write ( & mut serialized_scorer) . unwrap ( ) ;
31273094
3128- SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3129-
31303095 let mut serialized_scorer = io:: Cursor :: new ( & serialized_scorer) ;
3131- let deserialized_scorer =
3096+ let mut deserialized_scorer =
31323097 <ProbabilisticScorer >:: read ( & mut serialized_scorer, ( decay_params, & network_graph, & logger) ) . unwrap ( ) ;
3098+ if !decay_before_reload {
3099+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3100+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3101+ deserialized_scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3102+ }
31333103 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 473 ) ;
31343104
31353105 scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
31363106 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 300 ) ;
31373107
31383108 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3109+ deserialized_scorer. decay_liquidity_certainty ( Duration :: from_secs ( 20 ) ) ;
31393110 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 370 ) ;
31403111 }
31413112
3113+ #[ test]
3114+ fn decays_persisted_liquidity_bounds ( ) {
3115+ do_decays_persisted_liquidity_bounds ( false ) ;
3116+ do_decays_persisted_liquidity_bounds ( true ) ;
3117+ }
3118+
31423119 #[ test]
31433120 fn scores_realistic_payments ( ) {
31443121 // Shows the scores of "realistic" sends of 100k sats over channels of 1-10m sats (with a
@@ -3402,11 +3379,11 @@ mod tests {
34023379 // Advance the time forward 16 half-lives (which the docs claim will ensure all data is
34033380 // gone), and check that we're back to where we started.
34043381 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 16 ) ) ;
3382+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 16 ) ) ;
34053383 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 168 ) ;
3406- // Once fully decayed we still have data, but its all-0s. In the future we may remove the
3407- // data entirely instead.
3384+ // Once fully decayed we still have data, so we get 0s for the buckets.
34083385 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3409- None ) ;
3386+ Some ( ( [ 0 ; 32 ] , [ 0 ; 32 ] ) ) ) ;
34103387 assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 , & params) , None ) ;
34113388
34123389 let mut usage = ChannelUsage {
@@ -3417,7 +3394,7 @@ mod tests {
34173394 scorer. payment_path_failed ( & payment_path_for_amount ( 1 ) , 42 , Duration :: from_secs ( 10 * 16 ) ) ;
34183395 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 2050 ) ;
34193396 usage. inflight_htlc_msat = 0 ;
3420- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 866 ) ;
3397+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 2048 ) ;
34213398
34223399 let usage = ChannelUsage {
34233400 amount_msat : 1 ,
@@ -3428,6 +3405,12 @@ mod tests {
34283405
34293406 // Advance to decay all liquidity offsets to zero.
34303407 SinceEpoch :: advance ( Duration :: from_secs ( 60 * 60 * 10 ) ) ;
3408+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * ( 16 + 60 * 60 ) ) ) ;
3409+
3410+ // Once even the bounds have decayed information about the channel should be removed
3411+ // entirely.
3412+ assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3413+ None ) ;
34313414
34323415 // Use a path in the opposite direction, which have zero for htlc_maximum_msat. This will
34333416 // ensure that the effective capacity is zero to test division-by-zero edge cases.
0 commit comments