Skip to content

Commit 71e3f20

Browse files
committed
Add probing_diversity_penalty_msat to the scorer parameters
When doing background probing, its important to get a diverse view of the network graph to ensure you have as many options when pathfinding as possible. Sadly, the naive probing we currently recommend users do does not accomplish that - using the same success-optimized pathfinder when sending as when probing results in always testing the same (good) path over and over and over again. Instead, here, we add a `probing_diversity_penalty_msat` parameter to the scorer, allowing us to penalize channels for which we already have recent data. It applies a penalty which scales with the square of the inverse time since the channel was last updated, up to one day.
1 parent 88951fc commit 71e3f20

File tree

1 file changed

+86
-2
lines changed

1 file changed

+86
-2
lines changed

lightning/src/routing/scoring.rs

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,9 @@ where L::Target: Logger {
476476
network_graph: G,
477477
logger: L,
478478
channel_liquidities: ChannelLiquidities,
479+
/// The last time we were given via a [`ScoreUpdate`] method. This does not imply that we've
480+
/// decayed every liquidity bound up to that time.
481+
last_duration_since_epoch: Duration,
479482
}
480483
/// Container for live and historical liquidity bounds for each channel.
481484
#[derive(Clone)]
@@ -745,6 +748,22 @@ pub struct ProbabilisticScoringFeeParameters {
745748
///
746749
/// Default value: false
747750
pub linear_success_probability: bool,
751+
752+
/// In order to ensure we have knowledge for as many paths as possible, when probing it makes
753+
/// sense to bias away from channels for which we have very recent data.
754+
///
755+
/// This value is a penalty that is applied based on the last time that we updated the bounds
756+
/// on the available liquidity in a channel. The specified value is the maximum penalty that
757+
/// will be applied.
758+
///
759+
/// It obviously does not make sense to assign a non-0 value here unless you are using the
760+
/// pathfinding result for background probing.
761+
///
762+
/// Specifically, the following penalty is applied
763+
/// `probing_diversity_penalty_msat * max(0, (86400 - current time + last update))^2 / 86400^2` is
764+
///
765+
/// Default value: 0
766+
pub probing_diversity_penalty_msat: u64,
748767
}
749768

750769
impl Default for ProbabilisticScoringFeeParameters {
@@ -760,6 +779,7 @@ impl Default for ProbabilisticScoringFeeParameters {
760779
historical_liquidity_penalty_multiplier_msat: 10_000,
761780
historical_liquidity_penalty_amount_multiplier_msat: 1_250,
762781
linear_success_probability: false,
782+
probing_diversity_penalty_msat: 0,
763783
}
764784
}
765785
}
@@ -814,6 +834,7 @@ impl ProbabilisticScoringFeeParameters {
814834
anti_probing_penalty_msat: 0,
815835
considered_impossible_penalty_msat: 0,
816836
linear_success_probability: true,
837+
probing_diversity_penalty_msat: 0,
817838
}
818839
}
819840
}
@@ -927,6 +948,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ProbabilisticScorer<G, L> whe
927948
network_graph,
928949
logger,
929950
channel_liquidities: ChannelLiquidities::new(),
951+
last_duration_since_epoch: Duration::from_secs(0),
930952
}
931953
}
932954

@@ -1383,7 +1405,7 @@ DirectedChannelLiquidity< L, HT, T> {
13831405
/// Returns a liquidity penalty for routing the given HTLC `amount_msat` through the channel in
13841406
/// this direction.
13851407
fn penalty_msat(
1386-
&self, amount_msat: u64, inflight_htlc_msat: u64,
1408+
&self, amount_msat: u64, inflight_htlc_msat: u64, last_duration_since_epoch: Duration,
13871409
score_params: &ProbabilisticScoringFeeParameters,
13881410
) -> u64 {
13891411
let total_inflight_amount_msat = amount_msat.saturating_add(inflight_htlc_msat);
@@ -1465,6 +1487,13 @@ DirectedChannelLiquidity< L, HT, T> {
14651487
}
14661488
}
14671489

1490+
if score_params.probing_diversity_penalty_msat != 0 {
1491+
let time_since_update = last_duration_since_epoch.saturating_sub(*self.last_datapoint);
1492+
let mul = Duration::from_secs(60 * 60 * 24).saturating_sub(time_since_update).as_secs();
1493+
let penalty = score_params.probing_diversity_penalty_msat.saturating_mul(mul * mul);
1494+
res = res.saturating_add(penalty / ((60 * 60 * 24) * (60 * 60 * 24)));
1495+
}
1496+
14681497
res
14691498
}
14701499

@@ -1616,11 +1645,12 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreLookUp for Probabilistic
16161645
}
16171646

16181647
let capacity_msat = usage.effective_capacity.as_msat();
1648+
let time = self.last_duration_since_epoch;
16191649
self.channel_liquidities
16201650
.get(scid)
16211651
.unwrap_or(&ChannelLiquidity::new(Duration::ZERO))
16221652
.as_directed(&source, &target, capacity_msat)
1623-
.penalty_msat(usage.amount_msat, usage.inflight_htlc_msat, score_params)
1653+
.penalty_msat(usage.amount_msat, usage.inflight_htlc_msat, time, score_params)
16241654
.saturating_add(anti_probing_penalty_msat)
16251655
.saturating_add(base_penalty_msat)
16261656
}
@@ -1666,6 +1696,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
16661696
}
16671697
if at_failed_channel { break; }
16681698
}
1699+
self.last_duration_since_epoch = duration_since_epoch;
16691700
}
16701701

16711702
fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
@@ -1693,6 +1724,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
16931724
hop.short_channel_id);
16941725
}
16951726
}
1727+
self.last_duration_since_epoch = duration_since_epoch;
16961728
}
16971729

16981730
fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
@@ -1705,6 +1737,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
17051737

17061738
fn time_passed(&mut self, duration_since_epoch: Duration) {
17071739
self.channel_liquidities.time_passed(duration_since_epoch, self.decay_params);
1740+
self.last_duration_since_epoch = duration_since_epoch;
17081741
}
17091742
}
17101743

@@ -2361,11 +2394,16 @@ ReadableArgs<(ProbabilisticScoringDecayParameters, G, L)> for ProbabilisticScore
23612394
) -> Result<Self, DecodeError> {
23622395
let (decay_params, network_graph, logger) = args;
23632396
let channel_liquidities = ChannelLiquidities::read(r)?;
2397+
let mut last_duration_since_epoch = Duration::from_secs(0);
2398+
for (_, liq) in channel_liquidities.0.iter() {
2399+
last_duration_since_epoch = cmp::max(last_duration_since_epoch, liq.last_updated);
2400+
}
23642401
Ok(Self {
23652402
decay_params,
23662403
network_graph,
23672404
logger,
23682405
channel_liquidities,
2406+
last_duration_since_epoch,
23692407
})
23702408
}
23712409
}
@@ -4060,6 +4098,52 @@ mod tests {
40604098
combined_scorer.scorer.estimated_channel_liquidity_range(42, &target_node_id());
40614099
assert_eq!(liquidity_range.unwrap(), (0, 0));
40624100
}
4101+
4102+
#[test]
4103+
fn probes_for_diversity() {
4104+
// Tests the probing_diversity_penalty_msat is applied
4105+
let logger = TestLogger::new();
4106+
let network_graph = network_graph(&logger);
4107+
let params = ProbabilisticScoringFeeParameters {
4108+
probing_diversity_penalty_msat: 1_000_000,
4109+
..ProbabilisticScoringFeeParameters::zero_penalty()
4110+
};
4111+
let decay_params = ProbabilisticScoringDecayParameters {
4112+
liquidity_offset_half_life: Duration::from_secs(10),
4113+
..ProbabilisticScoringDecayParameters::zero_penalty()
4114+
};
4115+
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
4116+
let source = source_node_id();
4117+
4118+
let usage = ChannelUsage {
4119+
amount_msat: 512,
4120+
inflight_htlc_msat: 0,
4121+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
4122+
};
4123+
let channel = network_graph.read_only().channel(42).unwrap().to_owned();
4124+
let (info, _) = channel.as_directed_from(&source).unwrap();
4125+
let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
4126+
info,
4127+
short_channel_id: 42,
4128+
});
4129+
4130+
// Apply some update to set the last-update time to now
4131+
scorer.payment_path_failed(&payment_path_for_amount(1000), 42, Duration::ZERO);
4132+
4133+
// If no time has passed, we get the full probing_diversity_penalty_msat
4134+
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 1_000_000);
4135+
4136+
// As time passes the penalty decreases.
4137+
scorer.time_passed(Duration::from_secs(1));
4138+
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 999_976);
4139+
4140+
scorer.time_passed(Duration::from_secs(2));
4141+
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 999_953);
4142+
4143+
// Once we've gotten halfway through the day our penalty is 1/4 the configured value.
4144+
scorer.time_passed(Duration::from_secs(86400/2));
4145+
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 250_000);
4146+
}
40634147
}
40644148

40654149
#[cfg(ldk_bench)]

0 commit comments

Comments
 (0)