@@ -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,
@@ -2019,22 +2008,20 @@ mod bucketed_history {
20192008 }
20202009
20212010 impl < D : Deref < Target = HistoricalBucketRangeTracker > > HistoricalMinMaxBuckets < D > {
2022- pub ( super ) fn get_decayed_buckets < T : Time > ( & self , now : T , offset_history_last_updated : T , half_life : Duration )
2023- -> Option < ( [ u16 ; 32 ] , [ u16 ; 32 ] ) > {
2024- let ( _, required_decays) = self . get_total_valid_points ( now, offset_history_last_updated, half_life) ?;
2025-
2026- let mut min_buckets = * self . min_liquidity_offset_history ;
2027- min_buckets. time_decay_data ( required_decays) ;
2028- let mut max_buckets = * self . max_liquidity_offset_history ;
2029- max_buckets. time_decay_data ( required_decays) ;
2030- Some ( ( min_buckets. buckets , max_buckets. buckets ) )
2031- }
20322011 #[ inline]
2033- pub ( super ) fn get_total_valid_points < T : Time > ( & self , now : T , offset_history_last_updated : T , half_life : Duration )
2034- -> Option < ( u64 , u32 ) > {
2035- let required_decays = now. duration_since ( offset_history_last_updated) . as_secs ( )
2036- . checked_div ( half_life. as_secs ( ) )
2037- . map_or ( u32:: max_value ( ) , |decays| cmp:: min ( decays, u32:: max_value ( ) as u64 ) as u32 ) ;
2012+ pub ( super ) fn calculate_success_probability_times_billion (
2013+ & self , params : & ProbabilisticScoringFeeParameters , amount_msat : u64 ,
2014+ capacity_msat : u64
2015+ ) -> Option < u64 > {
2016+ // If historical penalties are enabled, we try to calculate a probability of success
2017+ // given our historical distribution of min- and max-liquidity bounds in a channel.
2018+ // To do so, we walk the set of historical liquidity bucket (min, max) combinations
2019+ // (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2020+ // state). For each pair, we calculate the probability as if the bucket's corresponding
2021+ // min- and max- liquidity bounds were our current liquidity bounds and then multiply
2022+ // that probability by the weight of the selected buckets.
2023+ let payment_pos = amount_to_pos ( amount_msat, capacity_msat) ;
2024+ if payment_pos >= POSITION_TICKS { return None ; }
20382025
20392026 let mut total_valid_points_tracked = 0 ;
20402027 for ( min_idx, min_bucket) in self . min_liquidity_offset_history . buckets . iter ( ) . enumerate ( ) {
@@ -2046,33 +2033,10 @@ mod bucketed_history {
20462033 // If the total valid points is smaller than 1.0 (i.e. 32 in our fixed-point scheme),
20472034 // treat it as if we were fully decayed.
20482035 const FULLY_DECAYED : u16 = BUCKET_FIXED_POINT_ONE * BUCKET_FIXED_POINT_ONE ;
2049- if total_valid_points_tracked. checked_shr ( required_decays ) . unwrap_or ( 0 ) < FULLY_DECAYED . into ( ) {
2036+ if total_valid_points_tracked < FULLY_DECAYED . into ( ) {
20502037 return None ;
20512038 }
20522039
2053- Some ( ( total_valid_points_tracked, required_decays) )
2054- }
2055-
2056- #[ inline]
2057- pub ( super ) fn calculate_success_probability_times_billion < T : Time > (
2058- & self , now : T , offset_history_last_updated : T , half_life : Duration ,
2059- params : & ProbabilisticScoringFeeParameters , amount_msat : u64 , capacity_msat : u64
2060- ) -> Option < u64 > {
2061- // If historical penalties are enabled, we try to calculate a probability of success
2062- // given our historical distribution of min- and max-liquidity bounds in a channel.
2063- // To do so, we walk the set of historical liquidity bucket (min, max) combinations
2064- // (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2065- // state). For each pair, we calculate the probability as if the bucket's corresponding
2066- // min- and max- liquidity bounds were our current liquidity bounds and then multiply
2067- // that probability by the weight of the selected buckets.
2068- let payment_pos = amount_to_pos ( amount_msat, capacity_msat) ;
2069- if payment_pos >= POSITION_TICKS { return None ; }
2070-
2071- // Check if all our buckets are zero, once decayed and treat it as if we had no data. We
2072- // don't actually use the decayed buckets, though, as that would lose precision.
2073- let ( total_valid_points_tracked, _)
2074- = self . get_total_valid_points ( now, offset_history_last_updated, half_life) ?;
2075-
20762040 let mut cumulative_success_prob_times_billion = 0 ;
20772041 // Special-case the 0th min bucket - it generally means we failed a payment, so only
20782042 // consider the highest (i.e. largest-offset-from-max-capacity) max bucket for all
@@ -2928,19 +2892,9 @@ mod tests {
29282892 let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
29292893 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
29302894
2931- // No decay
2932- SinceEpoch :: advance ( Duration :: from_secs ( 4 ) ) ;
2933- let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
2934- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
2935- let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
2936- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 93 ) ;
2937- let usage = ChannelUsage { amount_msat : 768 , ..usage } ;
2938- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 1_479 ) ;
2939- let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
2940- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
2941-
29422895 // Half decay (i.e., three-quarter life)
2943- SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
2896+ SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2897+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 5 ) ) ;
29442898 let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
29452899 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 22 ) ;
29462900 let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
@@ -2952,6 +2906,7 @@ mod tests {
29522906
29532907 // One decay (i.e., half life)
29542908 SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2909+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
29552910 let usage = ChannelUsage { amount_msat : 64 , ..usage } ;
29562911 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29572912 let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
@@ -2963,6 +2918,7 @@ mod tests {
29632918
29642919 // Fully decay liquidity lower bound.
29652920 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 7 ) ) ;
2921+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 8 ) ) ;
29662922 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29672923 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29682924 let usage = ChannelUsage { amount_msat : 1 , ..usage } ;
@@ -2974,12 +2930,14 @@ mod tests {
29742930
29752931 // Fully decay liquidity upper bound.
29762932 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2933+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 9 ) ) ;
29772934 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29782935 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29792936 let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
29802937 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
29812938
29822939 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2940+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 10 ) ) ;
29832941 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29842942 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29852943 let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
@@ -3014,9 +2972,11 @@ mod tests {
30142972 // An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
30152973 // would cause an overflow.
30162974 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 64 ) ) ;
2975+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 64 ) ) ;
30172976 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 125 ) ;
30182977
30192978 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2979+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 65 ) ) ;
30202980 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 125 ) ;
30212981 }
30222982
@@ -3050,6 +3010,7 @@ mod tests {
30503010
30513011 // Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
30523012 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3013+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
30533014 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 291 ) ;
30543015
30553016 // Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
@@ -3064,6 +3025,7 @@ mod tests {
30643025
30653026 // Further decaying affects the lower bound more than the upper bound (128, 928).
30663027 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3028+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 20 ) ) ;
30673029 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 280 ) ;
30683030 }
30693031
@@ -3093,6 +3055,7 @@ mod tests {
30933055 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
30943056
30953057 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3058+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
30963059 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 473 ) ;
30973060
30983061 scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
@@ -3107,8 +3070,7 @@ mod tests {
31073070 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 300 ) ;
31083071 }
31093072
3110- #[ test]
3111- fn decays_persisted_liquidity_bounds ( ) {
3073+ fn do_decays_persisted_liquidity_bounds ( decay_before_reload : bool ) {
31123074 let logger = TestLogger :: new ( ) ;
31133075 let network_graph = network_graph ( & logger) ;
31143076 let params = ProbabilisticScoringFeeParameters {
@@ -3132,23 +3094,38 @@ mod tests {
31323094 scorer. payment_path_failed ( & payment_path_for_amount ( 500 ) , 42 , Duration :: ZERO ) ;
31333095 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
31343096
3097+ if decay_before_reload {
3098+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3099+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3100+ }
3101+
31353102 let mut serialized_scorer = Vec :: new ( ) ;
31363103 scorer. write ( & mut serialized_scorer) . unwrap ( ) ;
31373104
3138- SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3139-
31403105 let mut serialized_scorer = io:: Cursor :: new ( & serialized_scorer) ;
3141- let deserialized_scorer =
3106+ let mut deserialized_scorer =
31423107 <ProbabilisticScorer >:: read ( & mut serialized_scorer, ( decay_params, & network_graph, & logger) ) . unwrap ( ) ;
3108+ if !decay_before_reload {
3109+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3110+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3111+ deserialized_scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3112+ }
31433113 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 473 ) ;
31443114
31453115 scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
31463116 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 300 ) ;
31473117
31483118 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3119+ deserialized_scorer. decay_liquidity_certainty ( Duration :: from_secs ( 20 ) ) ;
31493120 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 370 ) ;
31503121 }
31513122
3123+ #[ test]
3124+ fn decays_persisted_liquidity_bounds ( ) {
3125+ do_decays_persisted_liquidity_bounds ( false ) ;
3126+ do_decays_persisted_liquidity_bounds ( true ) ;
3127+ }
3128+
31523129 #[ test]
31533130 fn scores_realistic_payments ( ) {
31543131 // Shows the scores of "realistic" sends of 100k sats over channels of 1-10m sats (with a
@@ -3412,11 +3389,11 @@ mod tests {
34123389 // Advance the time forward 16 half-lives (which the docs claim will ensure all data is
34133390 // gone), and check that we're back to where we started.
34143391 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 16 ) ) ;
3392+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 16 ) ) ;
34153393 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 168 ) ;
3416- // Once fully decayed we still have data, but its all-0s. In the future we may remove the
3417- // data entirely instead.
3394+ // Once fully decayed we still have data, so we get 0s for the buckets.
34183395 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3419- None ) ;
3396+ Some ( ( [ 0 ; 32 ] , [ 0 ; 32 ] ) ) ) ;
34203397 assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 , & params) , None ) ;
34213398
34223399 let mut usage = ChannelUsage {
@@ -3427,7 +3404,7 @@ mod tests {
34273404 scorer. payment_path_failed ( & payment_path_for_amount ( 1 ) , 42 , Duration :: from_secs ( 10 * 16 ) ) ;
34283405 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 2050 ) ;
34293406 usage. inflight_htlc_msat = 0 ;
3430- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 866 ) ;
3407+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 2048 ) ;
34313408
34323409 let usage = ChannelUsage {
34333410 amount_msat : 1 ,
@@ -3438,6 +3415,12 @@ mod tests {
34383415
34393416 // Advance to decay all liquidity offsets to zero.
34403417 SinceEpoch :: advance ( Duration :: from_secs ( 60 * 60 * 10 ) ) ;
3418+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * ( 16 + 60 * 60 ) ) ) ;
3419+
3420+ // Once even the bounds have decayed information about the channel should be removed
3421+ // entirely.
3422+ assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3423+ None ) ;
34413424
34423425 // Use a path in the opposite direction, which have zero for htlc_maximum_msat. This will
34433426 // ensure that the effective capacity is zero to test division-by-zero edge cases.
0 commit comments