@@ -735,15 +735,34 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
735
735
/// Note that this writes roughly one line per channel for which we have a liquidity estimate,
736
736
/// which may be a substantial amount of log output.
737
737
pub fn debug_log_liquidity_stats ( & self ) {
738
+ let now = T :: now ( ) ;
739
+
738
740
let graph = self . network_graph . read_only ( ) ;
739
741
for ( scid, liq) in self . channel_liquidities . iter ( ) {
740
742
if let Some ( chan_debug) = graph. channels ( ) . get ( scid) {
741
743
let log_direction = |source, target| {
742
744
if let Some ( ( directed_info, _) ) = chan_debug. as_directed_to ( target) {
743
745
let amt = directed_info. effective_capacity ( ) . as_msat ( ) ;
744
746
let dir_liq = liq. as_directed ( source, target, amt, & self . params ) ;
745
- log_debug ! ( self . logger, "Liquidity from {:?} to {:?} via {} is in the range ({}, {})" ,
746
- source, target, scid, dir_liq. min_liquidity_msat( ) , dir_liq. max_liquidity_msat( ) ) ;
747
+
748
+ let buckets = HistoricalMinMaxBuckets {
749
+ min_liquidity_offset_history : & dir_liq. min_liquidity_offset_history ,
750
+ max_liquidity_offset_history : & dir_liq. max_liquidity_offset_history ,
751
+ } ;
752
+ let ( min_buckets, max_buckets, _) = buckets. get_decayed_buckets ( now,
753
+ * dir_liq. last_updated , self . params . historical_no_updates_half_life ) ;
754
+
755
+ log_debug ! ( self . logger, core:: concat!(
756
+ "Liquidity from {:?} to {:?} via {} is in the range ({}, {}).\n " ,
757
+ "\t Historical min liquidity octile relative probabilities: {} {} {} {} {} {} {} {}\n " ,
758
+ "\t Historical max liquidity octile relative probabilities: {} {} {} {} {} {} {} {}" ) ,
759
+ source, target, scid, dir_liq. min_liquidity_msat( ) , dir_liq. max_liquidity_msat( ) ,
760
+ min_buckets[ 0 ] , min_buckets[ 1 ] , min_buckets[ 2 ] , min_buckets[ 3 ] ,
761
+ min_buckets[ 4 ] , min_buckets[ 5 ] , min_buckets[ 6 ] , min_buckets[ 7 ] ,
762
+ // Note that the liquidity buckets are an offset from the edge, so we
763
+ // inverse the max order to get the probabilities from zero.
764
+ max_buckets[ 7 ] , max_buckets[ 6 ] , max_buckets[ 5 ] , max_buckets[ 4 ] ,
765
+ max_buckets[ 3 ] , max_buckets[ 2 ] , max_buckets[ 1 ] , max_buckets[ 0 ] ) ;
747
766
} else {
748
767
log_debug ! ( self . logger, "No amount known for SCID {} from {:?} to {:?}" , scid, source, target) ;
749
768
}
@@ -774,6 +793,37 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
774
793
None
775
794
}
776
795
796
+ /// Query the historical estimated minimum and maximum liquidity available for sending a
797
+ /// payment over the channel with `scid` towards the given `target` node.
798
+ ///
799
+ /// Returns the relative probabilities of the liquidity bounds being in each octile,
800
+ /// lower-bound first.
801
+ pub fn historical_estimated_channel_liquidity_probabilities ( & self , scid : u64 , target : & NodeId )
802
+ -> Option < ( [ u16 ; 8 ] , [ u16 ; 8 ] ) > {
803
+ let graph = self . network_graph . read_only ( ) ;
804
+
805
+ if let Some ( chan) = graph. channels ( ) . get ( & scid) {
806
+ if let Some ( liq) = self . channel_liquidities . get ( & scid) {
807
+ if let Some ( ( directed_info, source) ) = chan. as_directed_to ( target) {
808
+ let amt = directed_info. effective_capacity ( ) . as_msat ( ) ;
809
+ let dir_liq = liq. as_directed ( source, target, amt, & self . params ) ;
810
+
811
+ let buckets = HistoricalMinMaxBuckets {
812
+ min_liquidity_offset_history : & dir_liq. min_liquidity_offset_history ,
813
+ max_liquidity_offset_history : & dir_liq. max_liquidity_offset_history ,
814
+ } ;
815
+ let ( min_buckets, mut max_buckets, _) = buckets. get_decayed_buckets ( T :: now ( ) ,
816
+ * dir_liq. last_updated , self . params . historical_no_updates_half_life ) ;
817
+ // Note that the liquidity buckets are an offset from the edge, so we inverse
818
+ // the max order to get the probabilities from zero.
819
+ max_buckets. reverse ( ) ;
820
+ return Some ( ( min_buckets, max_buckets) ) ;
821
+ }
822
+ }
823
+ }
824
+ None
825
+ }
826
+
777
827
/// Marks the node with the given `node_id` as banned, i.e.,
778
828
/// it will be avoided during path finding.
779
829
pub fn add_banned ( & mut self , node_id : & NodeId ) {
@@ -2688,19 +2738,32 @@ mod tests {
2688
2738
} ;
2689
2739
// With no historical data the normal liquidity penalty calculation is used.
2690
2740
assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage) , 47 ) ;
2741
+ assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
2742
+ None ) ;
2691
2743
2692
2744
scorer. payment_path_failed ( & payment_path_for_amount ( 1 ) . iter ( ) . collect :: < Vec < _ > > ( ) , 42 ) ;
2693
2745
assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage) , 2048 ) ;
2746
+ // The "it failed" increment is 32, where the probability should lie fully in the first
2747
+ // octile.
2748
+ assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
2749
+ Some ( ( [ 32 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 32 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ) ) ;
2694
2750
2695
2751
// Even after we tell the scorer we definitely have enough available liquidity, it will
2696
2752
// still remember that there was some failure in the past, and assign a non-0 penalty.
2697
2753
scorer. payment_path_failed ( & payment_path_for_amount ( 1000 ) . iter ( ) . collect :: < Vec < _ > > ( ) , 43 ) ;
2698
2754
assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage) , 198 ) ;
2755
+ // The first octile should be decayed just slightly and the last octile has a new point.
2756
+ assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
2757
+ Some ( ( [ 31 , 0 , 0 , 0 , 0 , 0 , 0 , 32 ] , [ 31 , 0 , 0 , 0 , 0 , 0 , 0 , 32 ] ) ) ) ;
2699
2758
2700
2759
// Advance the time forward 16 half-lives (which the docs claim will ensure all data is
2701
2760
// gone), and check that we're back to where we started.
2702
2761
SinceEpoch :: advance ( Duration :: from_secs ( 10 * 16 ) ) ;
2703
2762
assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage) , 47 ) ;
2763
+ // Once fully decayed we still have data, but its all-0s. In the future we may remove the
2764
+ // data entirely instead.
2765
+ assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
2766
+ Some ( ( [ 0 ; 8 ] , [ 0 ; 8 ] ) ) ) ;
2704
2767
}
2705
2768
2706
2769
#[ test]
0 commit comments