@@ -62,7 +62,7 @@ use util::logger::Logger;
62
62
use util:: time:: Time ;
63
63
64
64
use prelude:: * ;
65
- use core:: fmt;
65
+ use core:: { cmp , fmt} ;
66
66
use core:: cell:: { RefCell , RefMut } ;
67
67
use core:: convert:: TryInto ;
68
68
use core:: ops:: { Deref , DerefMut } ;
@@ -429,6 +429,16 @@ pub struct ProbabilisticScoringParameters {
429
429
/// [`liquidity_penalty_multiplier_msat`]: Self::liquidity_penalty_multiplier_msat
430
430
pub historical_liquidity_penalty_amount_multiplier_msat : u64 ,
431
431
432
+ /// If we aren't learning any new datapoints for a channel, the historical liquidity bounds
433
+ /// tracking can simply live on with increasingly stale data. Instead, when a channel has not
434
+ /// seen a liquidity estimate update for this amount of time, the historical datapoints are
435
+ /// decayed by half.
436
+ ///
437
+ /// Note that 16 or more half lives guarantees that all historical data will be wiped.
438
+ ///
439
+ /// Default value: 14 days
440
+ pub no_updates_historical_half_life : Duration ,
441
+
432
442
/// Manual penalties used for the given nodes. Allows to set a particular penalty for a given
433
443
/// node. Note that a manual penalty of `u64::max_value()` means the node would not ever be
434
444
/// considered during path finding.
@@ -502,6 +512,11 @@ impl HistoricalBucketRangeTracker {
502
512
}
503
513
}
504
514
}
515
+ fn decay_data ( & mut self , half_lives : u32 ) {
516
+ for e in self . buckets . iter_mut ( ) {
517
+ * e = e. checked_shr ( half_lives) . unwrap_or ( 0 ) ;
518
+ }
519
+ }
505
520
}
506
521
507
522
impl_writeable_tlv_based ! ( HistoricalBucketRangeTracker , { ( 0 , buckets, required) } ) ;
@@ -638,6 +653,7 @@ impl ProbabilisticScoringParameters {
638
653
liquidity_penalty_amount_multiplier_msat : 0 ,
639
654
historical_liquidity_penalty_multiplier_msat : 0 ,
640
655
historical_liquidity_penalty_amount_multiplier_msat : 0 ,
656
+ no_updates_historical_half_life : Duration :: from_secs ( 60 * 60 * 24 * 14 ) ,
641
657
manual_node_penalties : HashMap :: new ( ) ,
642
658
anti_probing_penalty_msat : 0 ,
643
659
considered_impossible_penalty_msat : 0 ,
@@ -663,6 +679,7 @@ impl Default for ProbabilisticScoringParameters {
663
679
liquidity_penalty_amount_multiplier_msat : 192 ,
664
680
historical_liquidity_penalty_multiplier_msat : 10_000 ,
665
681
historical_liquidity_penalty_amount_multiplier_msat : 64 ,
682
+ no_updates_historical_half_life : Duration :: from_secs ( 60 * 60 * 24 * 14 ) ,
666
683
manual_node_penalties : HashMap :: new ( ) ,
667
684
anti_probing_penalty_msat : 250 ,
668
685
considered_impossible_penalty_msat : 1_0000_0000_000 ,
@@ -802,14 +819,25 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
802
819
// sending less than 1/16th of a channel's capacity, or 1/8th if we used the top of the
803
820
// bucket.
804
821
let mut total_valid_points_tracked = 0 ;
822
+ let decays = self . now . duration_since ( * self . last_updated ) . as_secs ( )
823
+ . checked_div ( params. no_updates_historical_half_life . as_secs ( ) )
824
+ . map ( |decays| cmp:: min ( decays, u32:: max_value ( ) as u64 ) as u32 )
825
+ . unwrap_or ( u32:: max_value ( ) ) ;
826
+ // Rather than actually decaying the individual buckets, which would lose precision, we
827
+ // simply track whether any buckets have data which wouldn't be decayed to zero, and if
828
+ // there are none, treat it as if we had no data.
829
+ let mut is_fully_decayed = true ;
805
830
for ( min_idx, min_bucket) in self . min_liquidity_offset_history . buckets . iter ( ) . enumerate ( ) {
831
+ if min_bucket. checked_shr ( decays) . unwrap_or ( 0 ) > 0 { is_fully_decayed = false ; }
806
832
for max_bucket in self . max_liquidity_offset_history . buckets . iter ( ) . take ( 8 - min_idx) {
807
833
total_valid_points_tracked += ( * min_bucket as u64 ) * ( * max_bucket as u64 ) ;
834
+ if max_bucket. checked_shr ( decays) . unwrap_or ( 0 ) > 0 { is_fully_decayed = false ; }
808
835
}
809
836
}
810
- if total_valid_points_tracked == 0 {
811
- // If we don't have any valid points, redo the non-historical calculation with no
812
- // liquidity bounds tracked and the historical penalty multipliers.
837
+ if total_valid_points_tracked. checked_shr ( decays) . unwrap_or ( 0 ) < 32 * 32 || is_fully_decayed {
838
+ // If we don't have any valid points (or, once decayed, we have less than a full
839
+ // point), redo the non-historical calculation with no liquidity bounds tracked and
840
+ // the historical penalty multipliers.
813
841
let max_capacity = self . capacity_msat . saturating_sub ( amount_msat) . saturating_add ( 1 ) ;
814
842
let negative_log10_times_2048 =
815
843
approx:: negative_log10_times_2048 ( max_capacity, self . capacity_msat . saturating_add ( 1 ) ) ;
@@ -917,6 +945,12 @@ impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTrac
917
945
}
918
946
919
947
fn update_history_buckets ( & mut self ) {
948
+ let half_lives = self . now . duration_since ( * self . last_updated ) . as_secs ( )
949
+ . checked_div ( self . params . no_updates_historical_half_life . as_secs ( ) )
950
+ . map ( |v| v. try_into ( ) . unwrap_or ( u32:: max_value ( ) ) ) . unwrap_or ( u32:: max_value ( ) ) ;
951
+ self . min_liquidity_offset_history . decay_data ( half_lives) ;
952
+ self . max_liquidity_offset_history . decay_data ( half_lives) ;
953
+
920
954
debug_assert ! ( * self . min_liquidity_offset_msat <= self . capacity_msat) ;
921
955
self . min_liquidity_offset_history . track_datapoint (
922
956
( self . min_liquidity_offset_msat . saturating_sub ( 1 ) * 8 / self . capacity_msat )
@@ -935,8 +969,8 @@ impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTrac
935
969
} else {
936
970
self . decayed_offset_msat ( * self . max_liquidity_offset_msat )
937
971
} ;
938
- * self . last_updated = self . now ;
939
972
self . update_history_buckets ( ) ;
973
+ * self . last_updated = self . now ;
940
974
}
941
975
942
976
/// Adjusts the upper bound of the channel liquidity balance in this direction.
@@ -947,8 +981,8 @@ impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTrac
947
981
} else {
948
982
self . decayed_offset_msat ( * self . min_liquidity_offset_msat )
949
983
} ;
950
- * self . last_updated = self . now ;
951
984
self . update_history_buckets ( ) ;
985
+ * self . last_updated = self . now ;
952
986
}
953
987
}
954
988
0 commit comments