@@ -939,6 +939,9 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
939939 ///
940940 /// Because the datapoints are decayed slowly over time, values will eventually return to
941941 /// `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`].
942945 pub fn historical_estimated_channel_liquidity_probabilities ( & self , scid : u64 , target : & NodeId )
943946 -> Option < ( [ u16 ; 8 ] , [ u16 ; 8 ] ) > {
944947 let graph = self . network_graph . read_only ( ) ;
@@ -964,6 +967,38 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
964967 }
965968 None
966969 }
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+ }
9671002}
9681003
9691004impl < T : Time > ChannelLiquidity < T > {
@@ -2847,13 +2882,19 @@ mod tests {
28472882 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 47 ) ;
28482883 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
28492884 None ) ;
2885+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 42 ) ,
2886+ None ) ;
28502887
28512888 scorer. payment_path_failed ( & payment_path_for_amount ( 1 ) , 42 ) ;
28522889 assert_eq ! ( scorer. channel_penalty_msat( 42 , & source, & target, usage, & params) , 2048 ) ;
28532890 // The "it failed" increment is 32, where the probability should lie fully in the first
28542891 // octile.
28552892 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
28562893 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 ) ) ;
28572898
28582899 // Even after we tell the scorer we definitely have enough available liquidity, it will
28592900 // still remember that there was some failure in the past, and assign a non-0 penalty.
@@ -2863,6 +2904,17 @@ mod tests {
28632904 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
28642905 Some ( ( [ 31 , 0 , 0 , 0 , 0 , 0 , 0 , 32 ] , [ 31 , 0 , 0 , 0 , 0 , 0 , 0 , 32 ] ) ) ) ;
28652906
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+
28662918 // Advance the time forward 16 half-lives (which the docs claim will ensure all data is
28672919 // gone), and check that we're back to where we started.
28682920 SinceEpoch :: advance ( Duration :: from_secs ( 10 * 16 ) ) ;
@@ -2871,6 +2923,7 @@ mod tests {
28712923 // data entirely instead.
28722924 assert_eq ! ( scorer. historical_estimated_channel_liquidity_probabilities( 42 , & target) ,
28732925 Some ( ( [ 0 ; 8 ] , [ 0 ; 8 ] ) ) ) ;
2926+ assert_eq ! ( scorer. historical_estimated_payment_success_probability( 42 , & target, 1 ) , None ) ;
28742927
28752928 let mut usage = ChannelUsage {
28762929 amount_msat : 100 ,
0 commit comments