@@ -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,
@@ -2018,22 +2007,20 @@ mod bucketed_history {
20182007 }
20192008
20202009 impl < D : Deref < Target = HistoricalBucketRangeTracker > > HistoricalMinMaxBuckets < D > {
2021- pub ( super ) fn get_decayed_buckets < T : Time > ( & self , now : T , offset_history_last_updated : T , half_life : Duration )
2022- -> Option < ( [ u16 ; 32 ] , [ u16 ; 32 ] ) > {
2023- let ( _, required_decays) = self . get_total_valid_points ( now, offset_history_last_updated, half_life) ?;
2024-
2025- let mut min_buckets = * self . min_liquidity_offset_history ;
2026- min_buckets. time_decay_data ( required_decays) ;
2027- let mut max_buckets = * self . max_liquidity_offset_history ;
2028- max_buckets. time_decay_data ( required_decays) ;
2029- Some ( ( min_buckets. buckets , max_buckets. buckets ) )
2030- }
20312010 #[ inline]
2032- pub ( super ) fn get_total_valid_points < T : Time > ( & self , now : T , offset_history_last_updated : T , half_life : Duration )
2033- -> Option < ( u64 , u32 ) > {
2034- let required_decays = now. duration_since ( offset_history_last_updated) . as_secs ( )
2035- . checked_div ( half_life. as_secs ( ) )
2036- . map_or ( u32:: max_value ( ) , |decays| cmp:: min ( decays, u32:: max_value ( ) as u64 ) as u32 ) ;
2011+ pub ( super ) fn calculate_success_probability_times_billion (
2012+ & self , params : & ProbabilisticScoringFeeParameters , amount_msat : u64 ,
2013+ capacity_msat : u64
2014+ ) -> Option < u64 > {
2015+ // If historical penalties are enabled, we try to calculate a probability of success
2016+ // given our historical distribution of min- and max-liquidity bounds in a channel.
2017+ // To do so, we walk the set of historical liquidity bucket (min, max) combinations
2018+ // (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2019+ // state). For each pair, we calculate the probability as if the bucket's corresponding
2020+ // min- and max- liquidity bounds were our current liquidity bounds and then multiply
2021+ // that probability by the weight of the selected buckets.
2022+ let payment_pos = amount_to_pos ( amount_msat, capacity_msat) ;
2023+ if payment_pos >= POSITION_TICKS { return None ; }
20372024
20382025 let mut total_valid_points_tracked = 0 ;
20392026 for ( min_idx, min_bucket) in self . min_liquidity_offset_history . buckets . iter ( ) . enumerate ( ) {
@@ -2045,33 +2032,10 @@ mod bucketed_history {
20452032 // If the total valid points is smaller than 1.0 (i.e. 32 in our fixed-point scheme),
20462033 // treat it as if we were fully decayed.
20472034 const FULLY_DECAYED : u16 = BUCKET_FIXED_POINT_ONE * BUCKET_FIXED_POINT_ONE ;
2048- if total_valid_points_tracked. checked_shr ( required_decays ) . unwrap_or ( 0 ) < FULLY_DECAYED . into ( ) {
2035+ if total_valid_points_tracked < FULLY_DECAYED . into ( ) {
20492036 return None ;
20502037 }
20512038
2052- Some ( ( total_valid_points_tracked, required_decays) )
2053- }
2054-
2055- #[ inline]
2056- pub ( super ) fn calculate_success_probability_times_billion < T : Time > (
2057- & self , now : T , offset_history_last_updated : T , half_life : Duration ,
2058- params : & ProbabilisticScoringFeeParameters , amount_msat : u64 , capacity_msat : u64
2059- ) -> Option < u64 > {
2060- // If historical penalties are enabled, we try to calculate a probability of success
2061- // given our historical distribution of min- and max-liquidity bounds in a channel.
2062- // To do so, we walk the set of historical liquidity bucket (min, max) combinations
2063- // (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2064- // state). For each pair, we calculate the probability as if the bucket's corresponding
2065- // min- and max- liquidity bounds were our current liquidity bounds and then multiply
2066- // that probability by the weight of the selected buckets.
2067- let payment_pos = amount_to_pos ( amount_msat, capacity_msat) ;
2068- if payment_pos >= POSITION_TICKS { return None ; }
2069-
2070- // Check if all our buckets are zero, once decayed and treat it as if we had no data. We
2071- // don't actually use the decayed buckets, though, as that would lose precision.
2072- let ( total_valid_points_tracked, _)
2073- = self . get_total_valid_points ( now, offset_history_last_updated, half_life) ?;
2074-
20752039 let mut cumulative_success_prob_times_billion = 0 ;
20762040 // Special-case the 0th min bucket - it generally means we failed a payment, so only
20772041 // consider the highest (i.e. largest-offset-from-max-capacity) max bucket for all
@@ -2927,19 +2891,9 @@ mod tests {
29272891 let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
29282892 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
29292893
2930- // No decay
2931- SinceEpoch :: advance ( Duration :: from_secs ( 4 ) ) ;
2932- let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
2933- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
2934- let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
2935- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 93 ) ;
2936- let usage = ChannelUsage { amount_msat : 768 , ..usage } ;
2937- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 1_479 ) ;
2938- let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
2939- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
2940-
29412894 // Half decay (i.e., three-quarter life)
2942- SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
2895+ SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2896+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 5 ) ) ;
29432897 let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
29442898 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 22 ) ;
29452899 let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
@@ -2951,6 +2905,7 @@ mod tests {
29512905
29522906 // One decay (i.e., half life)
29532907 SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2908+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
29542909 let usage = ChannelUsage { amount_msat : 64 , ..usage } ;
29552910 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29562911 let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
@@ -2962,6 +2917,7 @@ mod tests {
29622917
29632918 // Fully decay liquidity lower bound.
29642919 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 7 ) ) ;
2920+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 8 ) ) ;
29652921 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29662922 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29672923 let usage = ChannelUsage { amount_msat : 1 , ..usage } ;
@@ -2973,12 +2929,14 @@ mod tests {
29732929
29742930 // Fully decay liquidity upper bound.
29752931 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2932+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 9 ) ) ;
29762933 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29772934 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29782935 let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
29792936 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
29802937
29812938 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2939+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 10 ) ) ;
29822940 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
29832941 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 0 ) ;
29842942 let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
@@ -3013,9 +2971,11 @@ mod tests {
30132971 // An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
30142972 // would cause an overflow.
30152973 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 64 ) ) ;
2974+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 64 ) ) ;
30162975 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 125 ) ;
30172976
30182977 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
2978+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 65 ) ) ;
30192979 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 125 ) ;
30202980 }
30212981
@@ -3049,6 +3009,7 @@ mod tests {
30493009
30503010 // Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
30513011 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3012+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
30523013 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 291 ) ;
30533014
30543015 // Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
@@ -3063,6 +3024,7 @@ mod tests {
30633024
30643025 // Further decaying affects the lower bound more than the upper bound (128, 928).
30653026 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3027+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 20 ) ) ;
30663028 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 280 ) ;
30673029 }
30683030
@@ -3092,6 +3054,7 @@ mod tests {
30923054 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
30933055
30943056 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3057+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
30953058 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 473 ) ;
30963059
30973060 scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
@@ -3106,8 +3069,7 @@ mod tests {
31063069 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 300 ) ;
31073070 }
31083071
3109- #[ test]
3110- fn decays_persisted_liquidity_bounds ( ) {
3072+ fn do_decays_persisted_liquidity_bounds ( decay_before_reload : bool ) {
31113073 let logger = TestLogger :: new ( ) ;
31123074 let network_graph = network_graph ( & logger) ;
31133075 let params = ProbabilisticScoringFeeParameters {
@@ -3131,23 +3093,38 @@ mod tests {
31313093 scorer. payment_path_failed ( & payment_path_for_amount ( 500 ) , 42 , Duration :: ZERO ) ;
31323094 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , u64 :: max_value( ) ) ;
31333095
3096+ if decay_before_reload {
3097+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3098+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3099+ }
3100+
31343101 let mut serialized_scorer = Vec :: new ( ) ;
31353102 scorer. write ( & mut serialized_scorer) . unwrap ( ) ;
31363103
3137- SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3138-
31393104 let mut serialized_scorer = io:: Cursor :: new ( & serialized_scorer) ;
3140- let deserialized_scorer =
3105+ let mut deserialized_scorer =
31413106 <ProbabilisticScorer >:: read ( & mut serialized_scorer, ( decay_params, & network_graph, & logger) ) . unwrap ( ) ;
3107+ if !decay_before_reload {
3108+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3109+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3110+ deserialized_scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3111+ }
31423112 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 473 ) ;
31433113
31443114 scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
31453115 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 300 ) ;
31463116
31473117 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3118+ deserialized_scorer. decay_liquidity_certainty ( Duration :: from_secs ( 20 ) ) ;
31483119 assert_eq ! ( deserialized_scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 370 ) ;
31493120 }
31503121
3122+ #[ test]
3123+ fn decays_persisted_liquidity_bounds ( ) {
3124+ do_decays_persisted_liquidity_bounds ( false ) ;
3125+ do_decays_persisted_liquidity_bounds ( true ) ;
3126+ }
3127+
31513128 #[ test]
31523129 fn scores_realistic_payments ( ) {
31533130 // Shows the scores of "realistic" sends of 100k sats over channels of 1-10m sats (with a
@@ -3411,11 +3388,11 @@ mod tests {
34113388 // Advance the time forward 16 half-lives (which the docs claim will ensure all data is
34123389 // gone), and check that we're back to where we started.
34133390 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 16 ) ) ;
3391+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 16 ) ) ;
34143392 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 168 ) ;
3415- // Once fully decayed we still have data, but its all-0s. In the future we may remove the
3416- // data entirely instead.
3393+ // Once fully decayed we still have data, so we get 0s for the buckets.
34173394 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3418- None ) ;
3395+ Some ( ( [ 0 ; 32 ] , [ 0 ; 32 ] ) ) ) ;
34193396 assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 , & params) , None ) ;
34203397
34213398 let mut usage = ChannelUsage {
@@ -3426,7 +3403,7 @@ mod tests {
34263403 scorer. payment_path_failed ( & payment_path_for_amount ( 1 ) , 42 , Duration :: from_secs ( 10 * 16 ) ) ;
34273404 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 2050 ) ;
34283405 usage. inflight_htlc_msat = 0 ;
3429- assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 866 ) ;
3406+ assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 2048 ) ;
34303407
34313408 let usage = ChannelUsage {
34323409 amount_msat : 1 ,
@@ -3437,6 +3414,12 @@ mod tests {
34373414
34383415 // Advance to decay all liquidity offsets to zero.
34393416 SinceEpoch :: advance ( Duration :: from_secs ( 60 * 60 * 10 ) ) ;
3417+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * ( 16 + 60 * 60 ) ) ) ;
3418+
3419+ // Once even the bounds have decayed information about the channel should be removed
3420+ // entirely.
3421+ assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3422+ None ) ;
34403423
34413424 // Use a path in the opposite direction, which have zero for htlc_maximum_msat. This will
34423425 // ensure that the effective capacity is zero to test division-by-zero edge cases.
0 commit comments