@@ -850,8 +850,6 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
850850 /// Note that this writes roughly one line per channel for which we have a liquidity estimate,
851851 /// which may be a substantial amount of log output.
852852 pub fn debug_log_liquidity_stats ( & self ) {
853- let now = T :: now ( ) ;
854-
855853 let graph = self . network_graph . read_only ( ) ;
856854 for ( scid, liq) in self . channel_liquidities . iter ( ) {
857855 if let Some ( chan_debug) = graph. channels ( ) . get ( scid) {
@@ -860,10 +858,8 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
860858 let amt = directed_info. effective_capacity ( ) . as_msat ( ) ;
861859 let dir_liq = liq. as_directed ( source, target, amt, self . decay_params ) ;
862860
863- let ( min_buckets, max_buckets) = dir_liq. liquidity_history
864- . get_decayed_buckets ( now, * dir_liq. offset_history_last_updated ,
865- self . decay_params . historical_no_updates_half_life )
866- . unwrap_or ( ( [ 0 ; 32 ] , [ 0 ; 32 ] ) ) ;
861+ let min_buckets = & dir_liq. liquidity_history . min_liquidity_offset_history . buckets ;
862+ let max_buckets = & dir_liq. liquidity_history . max_liquidity_offset_history . buckets ;
867863
868864 log_debug ! ( self . logger, core:: concat!(
869865 "Liquidity from {} to {} via {} is in the range ({}, {}).\n " ,
@@ -942,7 +938,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
942938 /// in the top and bottom bucket, and roughly with similar (recent) frequency.
943939 ///
944940 /// Because the datapoints are decayed slowly over time, values will eventually return to
945- /// `Some(([1 ; 32], [1 ; 32]))` and then to `None` once no datapoints remain .
941+ /// `Some(([0 ; 32], [0 ; 32]))` or `None` if no data remains for a channel .
946942 ///
947943 /// In order to fetch a single success probability from the buckets provided here, as used in
948944 /// the scoring model, see [`Self::historical_estimated_payment_success_probability`].
@@ -956,11 +952,8 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
956952 let amt = directed_info. effective_capacity ( ) . as_msat ( ) ;
957953 let dir_liq = liq. as_directed ( source, target, amt, self . decay_params ) ;
958954
959- let ( min_buckets, mut max_buckets) =
960- dir_liq. liquidity_history . get_decayed_buckets (
961- dir_liq. now , * dir_liq. offset_history_last_updated ,
962- self . decay_params . historical_no_updates_half_life
963- ) ?;
955+ let min_buckets = dir_liq. liquidity_history . min_liquidity_offset_history . buckets ;
956+ let mut max_buckets = dir_liq. liquidity_history . max_liquidity_offset_history . buckets ;
964957
965958 // Note that the liquidity buckets are an offset from the edge, so we inverse
966959 // the max order to get the probabilities from zero.
@@ -991,9 +984,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
991984 let dir_liq = liq. as_directed ( source, target, capacity_msat, self . decay_params ) ;
992985
993986 return dir_liq. liquidity_history . calculate_success_probability_times_billion (
994- dir_liq. now , * dir_liq. offset_history_last_updated ,
995- self . decay_params . historical_no_updates_half_life , & params, amount_msat,
996- capacity_msat
987+ & params, amount_msat, capacity_msat
997988 ) . map ( |p| p as f64 / ( 1024 * 1024 * 1024 ) as f64 ) ;
998989 }
999990 }
@@ -1214,9 +1205,7 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
12141205 score_params. historical_liquidity_penalty_amount_multiplier_msat != 0 {
12151206 if let Some ( cumulative_success_prob_times_billion) = self . liquidity_history
12161207 . calculate_success_probability_times_billion (
1217- self . now , * self . offset_history_last_updated ,
1218- self . decay_params . historical_no_updates_half_life , score_params, amount_msat,
1219- self . capacity_msat )
1208+ score_params, amount_msat, self . capacity_msat )
12201209 {
12211210 let historical_negative_log10_times_2048 = approx:: negative_log10_times_2048 ( cumulative_success_prob_times_billion + 1 , 1024 * 1024 * 1024 ) ;
12221211 res = res. saturating_add ( Self :: combined_penalty_msat ( amount_msat,
@@ -2027,22 +2016,20 @@ mod bucketed_history {
20272016 }
20282017
20292018 impl < D : Deref < Target = HistoricalBucketRangeTracker > > HistoricalMinMaxBuckets < D > {
2030- pub ( super ) fn get_decayed_buckets < T : Time > ( & self , now : T , offset_history_last_updated : T , half_life : Duration )
2031- -> Option < ( [ u16 ; 32 ] , [ u16 ; 32 ] ) > {
2032- let ( _, required_decays) = self . get_total_valid_points ( now, offset_history_last_updated, half_life) ?;
2033-
2034- let mut min_buckets = * self . min_liquidity_offset_history ;
2035- min_buckets. time_decay_data ( required_decays) ;
2036- let mut max_buckets = * self . max_liquidity_offset_history ;
2037- max_buckets. time_decay_data ( required_decays) ;
2038- Some ( ( min_buckets. buckets , max_buckets. buckets ) )
2039- }
20402019 #[ inline]
2041- pub ( super ) fn get_total_valid_points < T : Time > ( & self , now : T , offset_history_last_updated : T , half_life : Duration )
2042- -> Option < ( u64 , u32 ) > {
2043- let required_decays = now. duration_since ( offset_history_last_updated) . as_secs ( )
2044- . checked_div ( half_life. as_secs ( ) )
2045- . map_or ( u32:: max_value ( ) , |decays| cmp:: min ( decays, u32:: max_value ( ) as u64 ) as u32 ) ;
2020+ pub ( super ) fn calculate_success_probability_times_billion (
2021+ & self , params : & ProbabilisticScoringFeeParameters , amount_msat : u64 ,
2022+ capacity_msat : u64
2023+ ) -> Option < u64 > {
2024+ // If historical penalties are enabled, we try to calculate a probability of success
2025+ // given our historical distribution of min- and max-liquidity bounds in a channel.
2026+ // To do so, we walk the set of historical liquidity bucket (min, max) combinations
2027+ // (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2028+ // state). For each pair, we calculate the probability as if the bucket's corresponding
2029+ // min- and max- liquidity bounds were our current liquidity bounds and then multiply
2030+ // that probability by the weight of the selected buckets.
2031+ let payment_pos = amount_to_pos ( amount_msat, capacity_msat) ;
2032+ if payment_pos >= POSITION_TICKS { return None ; }
20462033
20472034 let mut total_valid_points_tracked = 0 ;
20482035 for ( min_idx, min_bucket) in self . min_liquidity_offset_history . buckets . iter ( ) . enumerate ( ) {
@@ -2054,33 +2041,10 @@ mod bucketed_history {
20542041 // If the total valid points is smaller than 1.0 (i.e. 32 in our fixed-point scheme),
20552042 // treat it as if we were fully decayed.
20562043 const FULLY_DECAYED : u16 = BUCKET_FIXED_POINT_ONE * BUCKET_FIXED_POINT_ONE ;
2057- if total_valid_points_tracked. checked_shr ( required_decays ) . unwrap_or ( 0 ) < FULLY_DECAYED . into ( ) {
2044+ if total_valid_points_tracked < FULLY_DECAYED . into ( ) {
20582045 return None ;
20592046 }
20602047
2061- Some ( ( total_valid_points_tracked, required_decays) )
2062- }
2063-
2064- #[ inline]
2065- pub ( super ) fn calculate_success_probability_times_billion < T : Time > (
2066- & self , now : T , offset_history_last_updated : T , half_life : Duration ,
2067- params : & ProbabilisticScoringFeeParameters , amount_msat : u64 , capacity_msat : u64
2068- ) -> Option < u64 > {
2069- // If historical penalties are enabled, we try to calculate a probability of success
2070- // given our historical distribution of min- and max-liquidity bounds in a channel.
2071- // To do so, we walk the set of historical liquidity bucket (min, max) combinations
2072- // (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2073- // state). For each pair, we calculate the probability as if the bucket's corresponding
2074- // min- and max- liquidity bounds were our current liquidity bounds and then multiply
2075- // that probability by the weight of the selected buckets.
2076- let payment_pos = amount_to_pos ( amount_msat, capacity_msat) ;
2077- if payment_pos >= POSITION_TICKS { return None ; }
2078-
2079- // Check if all our buckets are zero, once decayed and treat it as if we had no data. We
2080- // don't actually use the decayed buckets, though, as that would lose precision.
2081- let ( total_valid_points_tracked, _)
2082- = self . get_total_valid_points ( now, offset_history_last_updated, half_life) ?;
2083-
20842048 let mut cumulative_success_prob_times_billion = 0 ;
20852049 // Special-case the 0th min bucket - it generally means we failed a payment, so only
20862050 // consider the highest (i.e. largest-offset-from-max-capacity) max bucket for all
@@ -3012,19 +2976,9 @@ mod tests {
30122976 let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
30132977 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , u64 :: max_value( ) ) ;
30142978
3015- // No decay
3016- SinceEpoch :: advance ( Duration :: from_secs ( 4 ) ) ;
3017- let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
3018- assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 0 ) ;
3019- let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
3020- assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 93 ) ;
3021- let usage = ChannelUsage { amount_msat : 768 , ..usage } ;
3022- assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 1_479 ) ;
3023- let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
3024- assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , u64 :: max_value( ) ) ;
3025-
30262979 // Half decay (i.e., three-quarter life)
3027- SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
2980+ SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2981+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 5 ) ) ;
30282982 let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
30292983 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 22 ) ;
30302984 let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
@@ -3036,6 +2990,7 @@ mod tests {
30362990
30372991 // One decay (i.e., half life)
30382992 SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2993+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
30392994 let usage = ChannelUsage { amount_msat : 64 , ..usage } ;
30402995 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 0 ) ;
30412996 let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
@@ -3047,6 +3002,7 @@ mod tests {
30473002
30483003 // Fully decay liquidity lower bound.
30493004 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 7 ) ) ;
3005+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 8 ) ) ;
30503006 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
30513007 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 0 ) ;
30523008 let usage = ChannelUsage { amount_msat : 1 , ..usage } ;
@@ -3058,12 +3014,14 @@ mod tests {
30583014
30593015 // Fully decay liquidity upper bound.
30603016 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3017+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 9 ) ) ;
30613018 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
30623019 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 0 ) ;
30633020 let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
30643021 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , u64 :: max_value( ) ) ;
30653022
30663023 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3024+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 10 ) ) ;
30673025 let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
30683026 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 0 ) ;
30693027 let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
@@ -3103,9 +3061,11 @@ mod tests {
31033061 // An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
31043062 // would cause an overflow.
31053063 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 64 ) ) ;
3064+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 64 ) ) ;
31063065 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 125 ) ;
31073066
31083067 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3068+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 65 ) ) ;
31093069 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 125 ) ;
31103070 }
31113071
@@ -3144,6 +3104,7 @@ mod tests {
31443104
31453105 // Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
31463106 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3107+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
31473108 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 291 ) ;
31483109
31493110 // Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
@@ -3158,6 +3119,7 @@ mod tests {
31583119
31593120 // Further decaying affects the lower bound more than the upper bound (128, 928).
31603121 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3122+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 20 ) ) ;
31613123 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 280 ) ;
31623124 }
31633125
@@ -3192,6 +3154,7 @@ mod tests {
31923154 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , u64 :: max_value( ) ) ;
31933155
31943156 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3157+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
31953158 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 473 ) ;
31963159
31973160 scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
@@ -3206,8 +3169,7 @@ mod tests {
32063169 assert_eq ! ( deserialized_scorer. channel_penalty_msat( & candidate, usage, & params) , 300 ) ;
32073170 }
32083171
3209- #[ test]
3210- fn decays_persisted_liquidity_bounds ( ) {
3172+ fn do_decays_persisted_liquidity_bounds ( decay_before_reload : bool ) {
32113173 let logger = TestLogger :: new ( ) ;
32123174 let network_graph = network_graph ( & logger) ;
32133175 let params = ProbabilisticScoringFeeParameters {
@@ -3236,23 +3198,38 @@ mod tests {
32363198 } ;
32373199 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , u64 :: max_value( ) ) ;
32383200
3201+ if decay_before_reload {
3202+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3203+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3204+ }
3205+
32393206 let mut serialized_scorer = Vec :: new ( ) ;
32403207 scorer. write ( & mut serialized_scorer) . unwrap ( ) ;
32413208
3242- SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3243-
32443209 let mut serialized_scorer = io:: Cursor :: new ( & serialized_scorer) ;
3245- let deserialized_scorer =
3210+ let mut deserialized_scorer =
32463211 <ProbabilisticScorer >:: read ( & mut serialized_scorer, ( decay_params, & network_graph, & logger) ) . unwrap ( ) ;
3212+ if !decay_before_reload {
3213+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3214+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3215+ deserialized_scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 ) ) ;
3216+ }
32473217 assert_eq ! ( deserialized_scorer. channel_penalty_msat( & candidate, usage, & params) , 473 ) ;
32483218
32493219 scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
32503220 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 300 ) ;
32513221
32523222 SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3223+ deserialized_scorer. decay_liquidity_certainty ( Duration :: from_secs ( 20 ) ) ;
32533224 assert_eq ! ( deserialized_scorer. channel_penalty_msat( & candidate, usage, & params) , 370 ) ;
32543225 }
32553226
3227+ #[ test]
3228+ fn decays_persisted_liquidity_bounds ( ) {
3229+ do_decays_persisted_liquidity_bounds ( false ) ;
3230+ do_decays_persisted_liquidity_bounds ( true ) ;
3231+ }
3232+
32563233 #[ test]
32573234 fn scores_realistic_payments ( ) {
32583235 // Shows the scores of "realistic" sends of 100k sats over channels of 1-10m sats (with a
@@ -3577,6 +3554,7 @@ mod tests {
35773554 // Advance the time forward 16 half-lives (which the docs claim will ensure all data is
35783555 // gone), and check that we're back to where we started.
35793556 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 16 ) ) ;
3557+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * 16 ) ) ;
35803558 {
35813559 let network_graph = network_graph. read_only ( ) ;
35823560 let channel = network_graph. channel ( 42 ) . unwrap ( ) ;
@@ -3591,7 +3569,7 @@ mod tests {
35913569 // Once fully decayed we still have data, but its all-0s. In the future we may remove the
35923570 // data entirely instead.
35933571 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3594- None ) ;
3572+ Some ( ( [ 0 ; 32 ] , [ 0 ; 32 ] ) ) ) ;
35953573 assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 , & params) , None ) ;
35963574
35973575 let mut usage = ChannelUsage {
@@ -3610,8 +3588,6 @@ mod tests {
36103588 } ;
36113589
36123590 assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 2050 ) ;
3613- usage. inflight_htlc_msat = 0 ;
3614- assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 866 ) ;
36153591
36163592 let usage = ChannelUsage {
36173593 amount_msat : 1 ,
@@ -3623,6 +3599,12 @@ mod tests {
36233599
36243600 // Advance to decay all liquidity offsets to zero.
36253601 SinceEpoch :: advance ( Duration :: from_secs ( 60 * 60 * 10 ) ) ;
3602+ scorer. decay_liquidity_certainty ( Duration :: from_secs ( 10 * ( 16 + 60 * 60 ) ) ) ;
3603+
3604+ // Once even the bounds have decayed information about the channel should be removed
3605+ // entirely.
3606+ assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3607+ None ) ;
36263608
36273609 // Use a path in the opposite direction, which have zero for htlc_maximum_msat. This will
36283610 // ensure that the effective capacity is zero to test division-by-zero edge cases.
0 commit comments