Skip to content

Commit f52c900

Browse files
committed
Expose historical bucket data via new accessors
Users should be able to view the data we use to score channels, so this exposes that data in new accessors. Fixes #1854.
1 parent 5b74735 commit f52c900

File tree

1 file changed

+65
-2
lines changed

1 file changed

+65
-2
lines changed

lightning/src/routing/scoring.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -735,15 +735,34 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
735735
/// Note that this writes roughly one line per channel for which we have a liquidity estimate,
736736
/// which may be a substantial amount of log output.
737737
pub fn debug_log_liquidity_stats(&self) {
738+
let now = T::now();
739+
738740
let graph = self.network_graph.read_only();
739741
for (scid, liq) in self.channel_liquidities.iter() {
740742
if let Some(chan_debug) = graph.channels().get(scid) {
741743
let log_direction = |source, target| {
742744
if let Some((directed_info, _)) = chan_debug.as_directed_to(target) {
743745
let amt = directed_info.effective_capacity().as_msat();
744746
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+
"\tHistorical min liquidity octile relative probabilities: {} {} {} {} {} {} {} {}\n",
758+
"\tHistorical 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]);
747766
} else {
748767
log_debug!(self.logger, "No amount known for SCID {} from {:?} to {:?}", scid, source, target);
749768
}
@@ -774,6 +793,37 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
774793
None
775794
}
776795

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+
777827
/// Marks the node with the given `node_id` as banned, i.e.,
778828
/// it will be avoided during path finding.
779829
pub fn add_banned(&mut self, node_id: &NodeId) {
@@ -2688,19 +2738,32 @@ mod tests {
26882738
};
26892739
// With no historical data the normal liquidity penalty calculation is used.
26902740
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 47);
2741+
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
2742+
None);
26912743

26922744
scorer.payment_path_failed(&payment_path_for_amount(1).iter().collect::<Vec<_>>(), 42);
26932745
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])));
26942750

26952751
// Even after we tell the scorer we definitely have enough available liquidity, it will
26962752
// still remember that there was some failure in the past, and assign a non-0 penalty.
26972753
scorer.payment_path_failed(&payment_path_for_amount(1000).iter().collect::<Vec<_>>(), 43);
26982754
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])));
26992758

27002759
// Advance the time forward 16 half-lives (which the docs claim will ensure all data is
27012760
// gone), and check that we're back to where we started.
27022761
SinceEpoch::advance(Duration::from_secs(10 * 16));
27032762
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])));
27042767
}
27052768

27062769
#[test]

0 commit comments

Comments
 (0)