@@ -939,6 +939,9 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
939
939
///
940
940
/// Because the datapoints are decayed slowly over time, values will eventually return to
941
941
/// `Some(([0; 8], [0; 8]))`.
942
+ ///
943
+ /// In order to convert this into a success probability, as used in the scoring model, see
944
+ /// [`Self::historical_estimated_payment_success_probability`].
942
945
pub fn historical_estimated_channel_liquidity_probabilities ( & self , scid : u64 , target : & NodeId )
943
946
-> Option < ( [ u16 ; 8 ] , [ u16 ; 8 ] ) > {
944
947
let graph = self . network_graph . read_only ( ) ;
@@ -964,6 +967,38 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
964
967
}
965
968
None
966
969
}
970
+
971
+ /// Query the probability of payment success (times 2^30) sending the given `amount_msat` over
972
+ /// the channel with `scid` towards the given `target` node, based on the historical estimated
973
+ /// liquidity bounds.
974
+ ///
975
+ /// These are the same bounds as returned by
976
+ /// [`Self::historical_estimated_channel_liquidity_probabilities`] (but not those returned by
977
+ /// [`Self::estimated_channel_liquidity_range`]).
978
+ pub fn historical_estimated_payment_success_probability (
979
+ & self , scid : u64 , target : & NodeId , amount_msat : u64 )
980
+ -> Option < u64 > {
981
+ let graph = self . network_graph . read_only ( ) ;
982
+
983
+ if let Some ( chan) = graph. channels ( ) . get ( & scid) {
984
+ if let Some ( liq) = self . channel_liquidities . get ( & scid) {
985
+ if let Some ( ( directed_info, source) ) = chan. as_directed_to ( target) {
986
+ let cap = directed_info. effective_capacity ( ) . as_msat ( ) ;
987
+ let dir_liq = liq. as_directed ( source, target, 0 , cap, self . decay_params ) ;
988
+
989
+ let buckets = HistoricalMinMaxBuckets {
990
+ min_liquidity_offset_history : & dir_liq. min_liquidity_offset_history ,
991
+ max_liquidity_offset_history : & dir_liq. max_liquidity_offset_history ,
992
+ } ;
993
+
994
+ return buckets. calculate_success_probability_times_billion ( T :: now ( ) ,
995
+ * dir_liq. last_updated , self . decay_params . historical_no_updates_half_life ,
996
+ amount_msat, directed_info. effective_capacity ( ) . as_msat ( ) ) ;
997
+ }
998
+ }
999
+ }
1000
+ None
1001
+ }
967
1002
}
968
1003
969
1004
impl < T : Time > ChannelLiquidity < T > {
@@ -2847,13 +2882,19 @@ mod tests {
2847
2882
assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 47 ) ;
2848
2883
assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
2849
2884
None ) ;
2885
+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 42 ) ,
2886
+ None ) ;
2850
2887
2851
2888
scorer. payment_path_failed ( & payment_path_for_amount ( 1 ) , 42 ) ;
2852
2889
assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 2048 ) ;
2853
2890
// The "it failed" increment is 32, where the probability should lie fully in the first
2854
2891
// octile.
2855
2892
assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
2856
2893
Some ( ( [ 32 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , [ 32 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ) ) ;
2894
+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 ) ,
2895
+ Some ( 1024 * 1024 * 1024 ) ) ;
2896
+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 500 ) ,
2897
+ Some ( 0 ) ) ;
2857
2898
2858
2899
// Even after we tell the scorer we definitely have enough available liquidity, it will
2859
2900
// still remember that there was some failure in the past, and assign a non-0 penalty.
@@ -2863,6 +2904,17 @@ mod tests {
2863
2904
assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
2864
2905
Some ( ( [ 31 , 0 , 0 , 0 , 0 , 0 , 0 , 32 ] , [ 31 , 0 , 0 , 0 , 0 , 0 , 0 , 32 ] ) ) ) ;
2865
2906
2907
+ // The exact success probability is a bit complicated and involves integer rounding, so we
2908
+ // simply check bounds here.
2909
+ let five_hundred_prob =
2910
+ scorer. historical_estimated_payment_success_probability ( 42 , & target, 500 ) . unwrap ( ) ;
2911
+ assert ! ( five_hundred_prob > 512 * 1024 * 1024 ) ; // 0.5
2912
+ assert ! ( five_hundred_prob < 532 * 1024 * 1024 ) ; // ~ 0.52
2913
+ let one_prob =
2914
+ scorer. historical_estimated_payment_success_probability ( 42 , & target, 1 ) . unwrap ( ) ;
2915
+ assert ! ( one_prob < 1024 * 1024 * 1024 ) ;
2916
+ assert ! ( one_prob > 1023 * 1024 * 1024 ) ;
2917
+
2866
2918
// Advance the time forward 16 half-lives (which the docs claim will ensure all data is
2867
2919
// gone), and check that we're back to where we started.
2868
2920
SinceEpoch :: advance ( Duration :: from_secs ( 10 * 16 ) ) ;
@@ -2871,6 +2923,7 @@ mod tests {
2871
2923
// data entirely instead.
2872
2924
assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
2873
2925
Some ( ( [ 0 ; 8 ] , [ 0 ; 8 ] ) ) ) ;
2926
+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 ) , None ) ;
2874
2927
2875
2928
let mut usage = ChannelUsage {
2876
2929
amount_msat : 100 ,
0 commit comments