Skip to content

Commit d3efe5c

Browse files
committed
Add a fallback-allowed param to historical success prob estimator
`historical_estimated_payment_success_probability` exposes the historical success probability estimator publicly, but only allows fetching data from channels where we have sufficient information. In the previous commit, `live_estimated_payment_success_probability` was added to enable querying the live bounds success probability estimator. Sadly, while the historical success probability estimator falls back to the live probability estimator, it does so with a different final parameter to `success_probability`, making `live_estimated_payment_success_probability` not useful for calculating the actual historical model output when we have insufficient data. Instead, here, we add a new parameter to `historical_estimated_payment_success_probability` which determines whether it will return fallback data from the live model instead.
1 parent b1770ba commit d3efe5c

File tree

1 file changed

+54
-32
lines changed

1 file changed

+54
-32
lines changed

lightning/src/routing/scoring.rs

+54-32
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
//! [`find_route`]: crate::routing::router::find_route
5353
5454
use crate::ln::msgs::DecodeError;
55-
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId};
55+
use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, NetworkGraph, NodeId};
5656
use crate::routing::router::{Path, CandidateRouteHop, PublicHopCandidate};
5757
use crate::routing::log_approx;
5858
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
@@ -956,32 +956,67 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ProbabilisticScorer<G, L> whe
956956
/// with `scid` towards the given `target` node, based on the historical estimated liquidity
957957
/// bounds.
958958
///
959-
/// Returns `None` if there is no (or insufficient) historical data for the given channel (or
960-
/// the provided `target` is not a party to the channel).
959+
/// Returns `None` if:
960+
/// - the given channel is not in the network graph, the provided `target` is not a party to
961+
/// the channel, or we don't have forwarding parameters for either direction in the channel.
962+
/// - `allow_fallback_estimation` is *not* set and there is no (or insufficient) historical
963+
/// data for the given channel.
961964
///
962965
/// These are the same bounds as returned by
963966
/// [`Self::historical_estimated_channel_liquidity_probabilities`] (but not those returned by
964967
/// [`Self::estimated_channel_liquidity_range`]).
965968
pub fn historical_estimated_payment_success_probability(
966-
&self, scid: u64, target: &NodeId, amount_msat: u64, params: &ProbabilisticScoringFeeParameters)
967-
-> Option<f64> {
969+
&self, scid: u64, target: &NodeId, amount_msat: u64, params: &ProbabilisticScoringFeeParameters,
970+
allow_fallback_estimation: bool,
971+
) -> Option<f64> {
968972
let graph = self.network_graph.read_only();
969973

970974
if let Some(chan) = graph.channels().get(&scid) {
971-
if let Some(liq) = self.channel_liquidities.get(&scid) {
972-
if let Some((directed_info, source)) = chan.as_directed_to(target) {
975+
if let Some((directed_info, source)) = chan.as_directed_to(target) {
976+
if let Some(liq) = self.channel_liquidities.get(&scid) {
973977
let capacity_msat = directed_info.effective_capacity().as_msat();
974978
let dir_liq = liq.as_directed(source, target, capacity_msat);
975979

976-
return dir_liq.liquidity_history.calculate_success_probability_times_billion(
980+
let res = dir_liq.liquidity_history.calculate_success_probability_times_billion(
977981
&params, amount_msat, capacity_msat
978982
).map(|p| p as f64 / (1024 * 1024 * 1024) as f64);
983+
if res.is_some() {
984+
return res;
985+
}
986+
}
987+
if allow_fallback_estimation {
988+
let amt = amount_msat;
989+
return Some(
990+
self.calc_live_prob(scid, source, target, directed_info, amt, params, true)
991+
);
979992
}
980993
}
981994
}
982995
None
983996
}
984997

998+
fn calc_live_prob(
999+
&self, scid: u64, source: &NodeId, target: &NodeId, directed_info: DirectedChannelInfo,
1000+
amt: u64, params: &ProbabilisticScoringFeeParameters,
1001+
min_zero_penalty: bool,
1002+
) -> f64 {
1003+
let capacity_msat = directed_info.effective_capacity().as_msat();
1004+
let dummy_liq = ChannelLiquidity::new(Duration::ZERO);
1005+
let liq = self.channel_liquidities.get(&scid)
1006+
.unwrap_or(&dummy_liq)
1007+
.as_directed(&source, &target, capacity_msat);
1008+
let min_liq = liq.min_liquidity_msat();
1009+
let max_liq = liq.max_liquidity_msat();
1010+
if amt <= liq.min_liquidity_msat() {
1011+
return 1.0;
1012+
} else if amt > liq.max_liquidity_msat() {
1013+
return 0.0;
1014+
}
1015+
let (num, den) =
1016+
success_probability(amt, min_liq, max_liq, capacity_msat, &params, min_zero_penalty);
1017+
num as f64 / den as f64
1018+
}
1019+
9851020
/// Query the probability of payment success sending the given `amount_msat` over the channel
9861021
/// with `scid` towards the given `target` node, based on the live estimated liquidity bounds.
9871022
///
@@ -994,20 +1029,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ProbabilisticScorer<G, L> whe
9941029

9951030
if let Some(chan) = graph.channels().get(&scid) {
9961031
if let Some((directed_info, source)) = chan.as_directed_to(target) {
997-
let capacity_msat = directed_info.effective_capacity().as_msat();
998-
let dummy_liq = ChannelLiquidity::new(Duration::ZERO);
999-
let liq = self.channel_liquidities.get(&scid)
1000-
.unwrap_or(&dummy_liq)
1001-
.as_directed(&source, &target, capacity_msat);
1002-
let min_liq = liq.min_liquidity_msat();
1003-
let max_liq = liq.max_liquidity_msat();
1004-
if amount_msat <= liq.min_liquidity_msat() {
1005-
return Some(1.0);
1006-
} else if amount_msat > liq.max_liquidity_msat() {
1007-
return Some(0.0);
1008-
}
1009-
let (num, den) = success_probability(amount_msat, min_liq, max_liq, capacity_msat, &params, false);
1010-
return Some(num as f64 / den as f64);
1032+
return Some(self.calc_live_prob(scid, source, target, directed_info, amount_msat, params, false));
10111033
}
10121034
}
10131035
None
@@ -3234,7 +3256,7 @@ mod tests {
32343256
}
32353257
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
32363258
None);
3237-
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, &params),
3259+
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, &params, false),
32383260
None);
32393261

32403262
scorer.payment_path_failed(&payment_path_for_amount(1), 42, Duration::ZERO);
@@ -3255,9 +3277,9 @@ mod tests {
32553277
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
32563278
Some(([32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
32573279
[0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])));
3258-
assert!(scorer.historical_estimated_payment_success_probability(42, &target, 1, &params)
3280+
assert!(scorer.historical_estimated_payment_success_probability(42, &target, 1, &params, false)
32593281
.unwrap() > 0.35);
3260-
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 500, &params),
3282+
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 500, &params, false),
32613283
Some(0.0));
32623284

32633285
// Even after we tell the scorer we definitely have enough available liquidity, it will
@@ -3282,11 +3304,11 @@ mod tests {
32823304
// The exact success probability is a bit complicated and involves integer rounding, so we
32833305
// simply check bounds here.
32843306
let five_hundred_prob =
3285-
scorer.historical_estimated_payment_success_probability(42, &target, 500, &params).unwrap();
3307+
scorer.historical_estimated_payment_success_probability(42, &target, 500, &params, false).unwrap();
32863308
assert!(five_hundred_prob > 0.59);
32873309
assert!(five_hundred_prob < 0.60);
32883310
let one_prob =
3289-
scorer.historical_estimated_payment_success_probability(42, &target, 1, &params).unwrap();
3311+
scorer.historical_estimated_payment_success_probability(42, &target, 1, &params, false).unwrap();
32903312
assert!(one_prob < 0.85);
32913313
assert!(one_prob > 0.84);
32923314

@@ -3308,7 +3330,7 @@ mod tests {
33083330
// data entirely instead.
33093331
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
33103332
Some(([0; 32], [0; 32])));
3311-
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 1, &params), None);
3333+
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 1, &params, false), None);
33123334

33133335
let usage = ChannelUsage {
33143336
amount_msat: 100,
@@ -3494,7 +3516,7 @@ mod tests {
34943516
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 1269);
34953517
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
34963518
None);
3497-
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, &params),
3519+
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, &params, false),
34983520
None);
34993521

35003522
// Fail to pay once, and then check the buckets and penalty.
@@ -3509,14 +3531,14 @@ mod tests {
35093531
Some(([32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
35103532
[0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])));
35113533
// The success probability estimate itself should be zero.
3512-
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params),
3534+
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params, false),
35133535
Some(0.0));
35143536

35153537
// Now test again with the amount in the bottom bucket.
35163538
amount_msat /= 2;
35173539
// The new amount is entirely within the only minimum bucket with score, so the probability
35183540
// we assign is 1/2.
3519-
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params),
3541+
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params, false),
35203542
Some(0.5));
35213543

35223544
// ...but once we see a failure, we consider the payment to be substantially less likely,
@@ -3526,7 +3548,7 @@ mod tests {
35263548
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
35273549
Some(([63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
35283550
[32, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])));
3529-
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params),
3551+
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params, false),
35303552
Some(0.0));
35313553
}
35323554
}

0 commit comments

Comments
 (0)