@@ -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,
@@ -2021,22 +2010,20 @@ mod bucketed_history {
20212010 }
20222011
20232012 impl < D : Deref < Target = HistoricalBucketRangeTracker > > HistoricalMinMaxBuckets < D > {
2024- pub ( super ) fn get_decayed_buckets < T : Time > ( & self , now : T , offset_history_last_updated : T , half_life : Duration )
2025- -> Option < ( [ u16 ; 32 ] , [ u16 ; 32 ] ) > {
2026- let ( _, required_decays) = self . get_total_valid_points ( now, offset_history_last_updated, half_life) ?;
2027-
2028- let mut min_buckets = * self . min_liquidity_offset_history ;
2029- min_buckets. time_decay_data ( required_decays) ;
2030- let mut max_buckets = * self . max_liquidity_offset_history ;
2031- max_buckets. time_decay_data ( required_decays) ;
2032- Some ( ( min_buckets. buckets , max_buckets. buckets ) )
2033- }
20342013 #[ inline]
2035- pub ( super ) fn get_total_valid_points < T : Time > ( & self , now : T , offset_history_last_updated : T , half_life : Duration )
2036- -> Option < ( u64 , u32 ) > {
2037- let required_decays = now. duration_since ( offset_history_last_updated) . as_secs ( )
2038- . checked_div ( half_life. as_secs ( ) )
2039- . map_or ( u32:: max_value ( ) , |decays| cmp:: min ( decays, u32:: max_value ( ) as u64 ) as u32 ) ;
2014+ pub ( super ) fn calculate_success_probability_times_billion (
2015+ & self , params : & ProbabilisticScoringFeeParameters , amount_msat : u64 ,
2016+ capacity_msat : u64
2017+ ) -> Option < u64 > {
2018+ // If historical penalties are enabled, we try to calculate a probability of success
2019+ // given our historical distribution of min- and max-liquidity bounds in a channel.
2020+ // To do so, we walk the set of historical liquidity bucket (min, max) combinations
2021+ // (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2022+ // state). For each pair, we calculate the probability as if the bucket's corresponding
2023+ // min- and max- liquidity bounds were our current liquidity bounds and then multiply
2024+ // that probability by the weight of the selected buckets.
2025+ let payment_pos = amount_to_pos ( amount_msat, capacity_msat) ;
2026+ if payment_pos >= POSITION_TICKS { return None ; }
20402027
20412028 let mut total_valid_points_tracked = 0 ;
20422029 for ( min_idx, min_bucket) in self . min_liquidity_offset_history . buckets . iter ( ) . enumerate ( ) {
@@ -2048,33 +2035,10 @@ mod bucketed_history {
20482035 // If the total valid points is smaller than 1.0 (i.e. 32 in our fixed-point scheme),
20492036 // treat it as if we were fully decayed.
20502037 const FULLY_DECAYED : u16 = BUCKET_FIXED_POINT_ONE * BUCKET_FIXED_POINT_ONE ;
2051- if total_valid_points_tracked. checked_shr ( required_decays ) . unwrap_or ( 0 ) < FULLY_DECAYED . into ( ) {
2038+ if total_valid_points_tracked < FULLY_DECAYED . into ( ) {
20522039 return None ;
20532040 }
20542041
2055- Some ( ( total_valid_points_tracked, required_decays) )
2056- }
2057-
2058- #[ inline]
2059- pub ( super ) fn calculate_success_probability_times_billion < T : Time > (
2060- & self , now : T , offset_history_last_updated : T , half_life : Duration ,
2061- params : & ProbabilisticScoringFeeParameters , amount_msat : u64 , capacity_msat : u64
2062- ) -> Option < u64 > {
2063- // If historical penalties are enabled, we try to calculate a probability of success
2064- // given our historical distribution of min- and max-liquidity bounds in a channel.
2065- // To do so, we walk the set of historical liquidity bucket (min, max) combinations
2066- // (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2067- // state). For each pair, we calculate the probability as if the bucket's corresponding
2068- // min- and max- liquidity bounds were our current liquidity bounds and then multiply
2069- // that probability by the weight of the selected buckets.
2070- let payment_pos = amount_to_pos ( amount_msat, capacity_msat) ;
2071- if payment_pos >= POSITION_TICKS { return None ; }
2072-
2073- // Check if all our buckets are zero, once decayed and treat it as if we had no data. We
2074- // don't actually use the decayed buckets, though, as that would lose precision.
2075- let ( total_valid_points_tracked, _)
2076- = self . get_total_valid_points ( now, offset_history_last_updated, half_life) ?;
2077-
20782042 let mut cumulative_success_prob_times_billion = 0 ;
20792043 // Special-case the 0th min bucket - it generally means we failed a payment, so only
20802044 // consider the highest (i.e. largest-offset-from-max-capacity) max bucket for all
@@ -2930,19 +2894,9 @@ mod tests {
29302894 let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
29312895 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
29322896
2933- // No decay
2934- SinceEpoch :: advance ( Duration :: from_secs ( 4 ) ) ;
2935- let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
2936- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
2937- let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
2938- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 93 ) ;
2939- let usage = ChannelUsage { amount_msat : 768 , ..usage } ;
2940- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 1_479 ) ;
2941- let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
2942- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
2943-
29442897 // Half decay (i.e., three-quarter life)
2945- SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
2898+ SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2899+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 5 ) ) ;
29462900 let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
29472901 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 22 ) ;
29482902 let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
@@ -2954,6 +2908,7 @@ mod tests {
29542908
29552909 // One decay (i.e., half life)
29562910 SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2911+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
29572912 let usage = ChannelUsage { amount_msat : 64 , ..usage } ;
29582913 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29592914 let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
@@ -2965,6 +2920,7 @@ mod tests {
29652920
29662921 // Fully decay liquidity lower bound.
29672922 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 7 ) ) ;
2923+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 8 ) ) ;
29682924 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29692925 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29702926 let usage = ChannelUsage { amount_msat : 1 , ..usage } ;
@@ -2976,12 +2932,14 @@ mod tests {
29762932
29772933 // Fully decay liquidity upper bound.
29782934 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2935+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 9 ) ) ;
29792936 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29802937 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29812938 let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
29822939 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
29832940
29842941 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2942+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 10 ) ) ;
29852943 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29862944 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29872945 let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
@@ -3016,9 +2974,11 @@ mod tests {
30162974 // An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
30172975 // would cause an overflow.
30182976 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 64 ) ) ;
2977+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 64 ) ) ;
30192978 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 125 ) ;
30202979
30212980 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2981+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 65 ) ) ;
30222982 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 125 ) ;
30232983 }
30242984
@@ -3052,6 +3012,7 @@ mod tests {
30523012
30533013 // Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
30543014 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3015+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
30553016 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 291 ) ;
30563017
30573018 // Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
@@ -3066,6 +3027,7 @@ mod tests {
30663027
30673028 // Further decaying affects the lower bound more than the upper bound (128, 928).
30683029 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3030+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 20 ) ) ;
30693031 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 280 ) ;
30703032 }
30713033
@@ -3095,6 +3057,7 @@ mod tests {
30953057 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
30963058
30973059 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3060+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
30983061 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 473 ) ;
30993062
31003063 scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
@@ -3109,8 +3072,7 @@ mod tests {
31093072 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 300 ) ;
31103073 }
31113074
3112- #[ test]
3113- fn decays_persisted_liquidity_bounds ( ) {
3075+ fn do_decays_persisted_liquidity_bounds ( decay_before_reload : bool ) {
31143076 let logger = TestLogger :: new ( ) ;
31153077 let network_graph = network_graph ( & logger) ;
31163078 let params = ProbabilisticScoringFeeParameters {
@@ -3134,23 +3096,38 @@ mod tests {
31343096 scorer. payment_path_failed ( & payment_path_for_amount ( 500 ) , 42 , Duration :: ZERO ) ;
31353097 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
31363098
3099+ if decay_before_reload {
3100+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3101+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3102+ }
3103+
31373104 let mut serialized_scorer = Vec :: new ( ) ;
31383105 scorer. write ( & mut serialized_scorer) . unwrap ( ) ;
31393106
3140- SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3141-
31423107 let mut serialized_scorer = io:: Cursor :: new ( & serialized_scorer) ;
3143- let deserialized_scorer =
3108+ let mut deserialized_scorer =
31443109 <ProbabilisticScorer >:: read ( & mut serialized_scorer, ( decay_params, & network_graph, & logger) ) . unwrap ( ) ;
3110+ if !decay_before_reload {
3111+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3112+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3113+ deserialized_scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3114+ }
31453115 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 473 ) ;
31463116
31473117 scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
31483118 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 300 ) ;
31493119
31503120 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3121+ deserialized_scorer. decay_liquidity_certainty ( Duration :: from_secs ( 20 ) ) ;
31513122 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 370 ) ;
31523123 }
31533124
3125+ #[ test]
3126+ fn decays_persisted_liquidity_bounds ( ) {
3127+ do_decays_persisted_liquidity_bounds ( false ) ;
3128+ do_decays_persisted_liquidity_bounds ( true ) ;
3129+ }
3130+
31543131 #[ test]
31553132 fn scores_realistic_payments ( ) {
31563133 // Shows the scores of "realistic" sends of 100k sats over channels of 1-10m sats (with a
@@ -3414,11 +3391,11 @@ mod tests {
34143391 // Advance the time forward 16 half-lives (which the docs claim will ensure all data is
34153392 // gone), and check that we're back to where we started.
34163393 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 16 ) ) ;
3394+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 16 ) ) ;
34173395 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 168 ) ;
3418- // Once fully decayed we still have data, but its all-0s. In the future we may remove the
3419- // data entirely instead.
3396+ // Once fully decayed we still have data, so we get 0s for the buckets.
34203397 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3421- None ) ;
3398+ Some ( ( [ 0 ; 32 ] , [ 0 ; 32 ] ) ) ) ;
34223399 assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 , & params) , None ) ;
34233400
34243401 let mut usage = ChannelUsage {
@@ -3429,7 +3406,7 @@ mod tests {
34293406 scorer. payment_path_failed ( & payment_path_for_amount ( 1 ) , 42 , Duration :: from_secs ( 10 * 16 ) ) ;
34303407 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 2050 ) ;
34313408 usage. inflight_htlc_msat = 0 ;
3432- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 866 ) ;
3409+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 2048 ) ;
34333410
34343411 let usage = ChannelUsage {
34353412 amount_msat : 1 ,
@@ -3440,6 +3417,12 @@ mod tests {
34403417
34413418 // Advance to decay all liquidity offsets to zero.
34423419 SinceEpoch :: advance ( Duration :: from_secs ( 60 * 60 * 10 ) ) ;
3420+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * ( 16 + 60 * 60 ) ) ) ;
3421+
3422+ // Once even the bounds have decayed information about the channel should be removed
3423+ // entirely.
3424+ assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3425+ None ) ;
34433426
34443427 // Use a path in the opposite direction, which have zero for htlc_maximum_msat. This will
34453428 // ensure that the effective capacity is zero to test division-by-zero edge cases.
0 commit comments