@@ -850,8 +850,6 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
850
850
/// Note that this writes roughly one line per channel for which we have a liquidity estimate,
851
851
/// which may be a substantial amount of log output.
852
852
pub fn debug_log_liquidity_stats ( & self ) {
853
- let now = T :: now ( ) ;
854
-
855
853
let graph = self . network_graph . read_only ( ) ;
856
854
for ( scid, liq) in self . channel_liquidities . iter ( ) {
857
855
if let Some ( chan_debug) = graph. channels ( ) . get ( scid) {
@@ -860,10 +858,8 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
860
858
let amt = directed_info. effective_capacity ( ) . as_msat ( ) ;
861
859
let dir_liq = liq. as_directed ( source, target, amt, self . decay_params ) ;
862
860
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 ;
867
863
868
864
log_debug ! ( self . logger, core:: concat!(
869
865
"Liquidity from {} to {} via {} is in the range ({}, {}).\n " ,
@@ -942,7 +938,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
942
938
/// in the top and bottom bucket, and roughly with similar (recent) frequency.
943
939
///
944
940
/// 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 .
946
942
///
947
943
/// In order to fetch a single success probability from the buckets provided here, as used in
948
944
/// 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
956
952
let amt = directed_info. effective_capacity ( ) . as_msat ( ) ;
957
953
let dir_liq = liq. as_directed ( source, target, amt, self . decay_params ) ;
958
954
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 ;
964
957
965
958
// Note that the liquidity buckets are an offset from the edge, so we inverse
966
959
// the max order to get the probabilities from zero.
@@ -991,9 +984,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
991
984
let dir_liq = liq. as_directed ( source, target, capacity_msat, self . decay_params ) ;
992
985
993
986
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
997
988
) . map ( |p| p as f64 / ( 1024 * 1024 * 1024 ) as f64 ) ;
998
989
}
999
990
}
@@ -1214,9 +1205,7 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
1214
1205
score_params. historical_liquidity_penalty_amount_multiplier_msat != 0 {
1215
1206
if let Some ( cumulative_success_prob_times_billion) = self . liquidity_history
1216
1207
. 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 )
1220
1209
{
1221
1210
let historical_negative_log10_times_2048 = approx:: negative_log10_times_2048 ( cumulative_success_prob_times_billion + 1 , 1024 * 1024 * 1024 ) ;
1222
1211
res = res. saturating_add ( Self :: combined_penalty_msat ( amount_msat,
@@ -2027,22 +2016,20 @@ mod bucketed_history {
2027
2016
}
2028
2017
2029
2018
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
- }
2040
2019
#[ 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 ; }
2046
2033
2047
2034
let mut total_valid_points_tracked = 0 ;
2048
2035
for ( min_idx, min_bucket) in self . min_liquidity_offset_history . buckets . iter ( ) . enumerate ( ) {
@@ -2054,33 +2041,10 @@ mod bucketed_history {
2054
2041
// If the total valid points is smaller than 1.0 (i.e. 32 in our fixed-point scheme),
2055
2042
// treat it as if we were fully decayed.
2056
2043
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 ( ) {
2058
2045
return None ;
2059
2046
}
2060
2047
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
-
2084
2048
let mut cumulative_success_prob_times_billion = 0 ;
2085
2049
// Special-case the 0th min bucket - it generally means we failed a payment, so only
2086
2050
// consider the highest (i.e. largest-offset-from-max-capacity) max bucket for all
@@ -3012,19 +2976,9 @@ mod tests {
3012
2976
let usage = ChannelUsage { amount_msat : 896 , ..usage } ;
3013
2977
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , u64 :: max_value( ) ) ;
3014
2978
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
-
3026
2979
// Half decay (i.e., three-quarter life)
3027
- SinceEpoch :: advance ( Duration :: from_secs ( 1 ) ) ;
2980
+ SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2981
+ scorer. time_passed ( Duration :: from_secs ( 5 ) ) ;
3028
2982
let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
3029
2983
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 22 ) ;
3030
2984
let usage = ChannelUsage { amount_msat : 256 , ..usage } ;
@@ -3036,6 +2990,7 @@ mod tests {
3036
2990
3037
2991
// One decay (i.e., half life)
3038
2992
SinceEpoch :: advance ( Duration :: from_secs ( 5 ) ) ;
2993
+ scorer. time_passed ( Duration :: from_secs ( 10 ) ) ;
3039
2994
let usage = ChannelUsage { amount_msat : 64 , ..usage } ;
3040
2995
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 0 ) ;
3041
2996
let usage = ChannelUsage { amount_msat : 128 , ..usage } ;
@@ -3047,6 +3002,7 @@ mod tests {
3047
3002
3048
3003
// Fully decay liquidity lower bound.
3049
3004
SinceEpoch :: advance ( Duration :: from_secs ( 10 * 7 ) ) ;
3005
+ scorer. time_passed ( Duration :: from_secs ( 10 * 8 ) ) ;
3050
3006
let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
3051
3007
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 0 ) ;
3052
3008
let usage = ChannelUsage { amount_msat : 1 , ..usage } ;
@@ -3058,12 +3014,14 @@ mod tests {
3058
3014
3059
3015
// Fully decay liquidity upper bound.
3060
3016
SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3017
+ scorer. time_passed ( Duration :: from_secs ( 10 * 9 ) ) ;
3061
3018
let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
3062
3019
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 0 ) ;
3063
3020
let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
3064
3021
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , u64 :: max_value( ) ) ;
3065
3022
3066
3023
SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3024
+ scorer. time_passed ( Duration :: from_secs ( 10 * 10 ) ) ;
3067
3025
let usage = ChannelUsage { amount_msat : 0 , ..usage } ;
3068
3026
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 0 ) ;
3069
3027
let usage = ChannelUsage { amount_msat : 1_024 , ..usage } ;
@@ -3103,9 +3061,11 @@ mod tests {
3103
3061
// An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
3104
3062
// would cause an overflow.
3105
3063
SinceEpoch :: advance ( Duration :: from_secs ( 10 * 64 ) ) ;
3064
+ scorer. time_passed ( Duration :: from_secs ( 10 * 64 ) ) ;
3106
3065
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 125 ) ;
3107
3066
3108
3067
SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3068
+ scorer. time_passed ( Duration :: from_secs ( 10 * 65 ) ) ;
3109
3069
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 125 ) ;
3110
3070
}
3111
3071
@@ -3144,6 +3104,7 @@ mod tests {
3144
3104
3145
3105
// Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
3146
3106
SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3107
+ scorer. time_passed ( Duration :: from_secs ( 10 ) ) ;
3147
3108
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 291 ) ;
3148
3109
3149
3110
// Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
@@ -3158,6 +3119,7 @@ mod tests {
3158
3119
3159
3120
// Further decaying affects the lower bound more than the upper bound (128, 928).
3160
3121
SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3122
+ scorer. time_passed ( Duration :: from_secs ( 20 ) ) ;
3161
3123
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 280 ) ;
3162
3124
}
3163
3125
@@ -3192,6 +3154,7 @@ mod tests {
3192
3154
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , u64 :: max_value( ) ) ;
3193
3155
3194
3156
SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3157
+ scorer. time_passed ( Duration :: from_secs ( 10 ) ) ;
3195
3158
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 473 ) ;
3196
3159
3197
3160
scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
@@ -3206,8 +3169,7 @@ mod tests {
3206
3169
assert_eq ! ( deserialized_scorer. channel_penalty_msat( & candidate, usage, & params) , 300 ) ;
3207
3170
}
3208
3171
3209
- #[ test]
3210
- fn decays_persisted_liquidity_bounds ( ) {
3172
+ fn do_decays_persisted_liquidity_bounds ( decay_before_reload : bool ) {
3211
3173
let logger = TestLogger :: new ( ) ;
3212
3174
let network_graph = network_graph ( & logger) ;
3213
3175
let params = ProbabilisticScoringFeeParameters {
@@ -3236,23 +3198,38 @@ mod tests {
3236
3198
} ;
3237
3199
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , u64 :: max_value( ) ) ;
3238
3200
3201
+ if decay_before_reload {
3202
+ SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3203
+ scorer. time_passed ( Duration :: from_secs ( 10 ) ) ;
3204
+ }
3205
+
3239
3206
let mut serialized_scorer = Vec :: new ( ) ;
3240
3207
scorer. write ( & mut serialized_scorer) . unwrap ( ) ;
3241
3208
3242
- SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3243
-
3244
3209
let mut serialized_scorer = io:: Cursor :: new ( & serialized_scorer) ;
3245
- let deserialized_scorer =
3210
+ let mut deserialized_scorer =
3246
3211
<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. time_passed ( Duration :: from_secs ( 10 ) ) ;
3215
+ deserialized_scorer. time_passed ( Duration :: from_secs ( 10 ) ) ;
3216
+ }
3247
3217
assert_eq ! ( deserialized_scorer. channel_penalty_msat( & candidate, usage, & params) , 473 ) ;
3248
3218
3249
3219
scorer. payment_path_failed ( & payment_path_for_amount ( 250 ) , 43 , Duration :: from_secs ( 10 ) ) ;
3250
3220
assert_eq ! ( scorer. channel_penalty_msat( & candidate, usage, & params) , 300 ) ;
3251
3221
3252
3222
SinceEpoch :: advance ( Duration :: from_secs ( 10 ) ) ;
3223
+ deserialized_scorer. time_passed ( Duration :: from_secs ( 20 ) ) ;
3253
3224
assert_eq ! ( deserialized_scorer. channel_penalty_msat( & candidate, usage, & params) , 370 ) ;
3254
3225
}
3255
3226
3227
+ #[ test]
3228
+ fn decays_persisted_liquidity_bounds ( ) {
3229
+ do_decays_persisted_liquidity_bounds ( false ) ;
3230
+ do_decays_persisted_liquidity_bounds ( true ) ;
3231
+ }
3232
+
3256
3233
#[ test]
3257
3234
fn scores_realistic_payments ( ) {
3258
3235
// Shows the scores of "realistic" sends of 100k sats over channels of 1-10m sats (with a
@@ -3577,6 +3554,7 @@ mod tests {
3577
3554
// Advance the time forward 16 half-lives (which the docs claim will ensure all data is
3578
3555
// gone), and check that we're back to where we started.
3579
3556
SinceEpoch :: advance ( Duration :: from_secs ( 10 * 16 ) ) ;
3557
+ scorer. time_passed ( Duration :: from_secs ( 10 * 16 ) ) ;
3580
3558
{
3581
3559
let network_graph = network_graph. read_only ( ) ;
3582
3560
let channel = network_graph. channel ( 42 ) . unwrap ( ) ;
@@ -3591,7 +3569,7 @@ mod tests {
3591
3569
// Once fully decayed we still have data, but its all-0s. In the future we may remove the
3592
3570
// data entirely instead.
3593
3571
assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
3594
- None ) ;
3572
+ Some ( ( [ 0 ; 32 ] , [ 0 ; 32 ] ) ) ) ;
3595
3573
assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 , & params) , None ) ;
3596
3574
3597
3575
let mut usage = ChannelUsage {
@@ -3610,8 +3588,6 @@ mod tests {
3610
3588
} ;
3611
3589
3612
3590
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 ) ;
3615
3591
3616
3592
let usage = ChannelUsage {
3617
3593
amount_msat : 1 ,
@@ -3623,6 +3599,12 @@ mod tests {
3623
3599
3624
3600
// Advance to decay all liquidity offsets to zero.
3625
3601
SinceEpoch :: advance ( Duration :: from_secs ( 60 * 60 * 10 ) ) ;
3602
+ scorer. time_passed ( 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 ) ;
3626
3608
3627
3609
// Use a path in the opposite direction, which have zero for htlc_maximum_msat. This will
3628
3610
// ensure that the effective capacity is zero to test division-by-zero edge cases.
0 commit comments