Skip to content

Commit 957f93f

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 983f3c5 commit 957f93f

File tree

1 file changed

+87
-3
lines changed

1 file changed

+87
-3
lines changed

lightning/src/routing/scoring.rs

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,9 @@ where L::Target: Logger {
475475
network_graph: G,
476476
logger: L,
477477
channel_liquidities: HashMap<u64, ChannelLiquidity>,
478+
/// The last time we were given via a [`ScoreUpdate`] method. This does not imply that we've
479+
/// decayed every liquidity bound up to that time.
480+
last_duration_since_epoch: Duration,
478481
}
479482

480483
/// Parameters for configuring [`ProbabilisticScorer`].
@@ -637,6 +640,22 @@ pub struct ProbabilisticScoringFeeParameters {
637640
///
638641
/// Default value: false
639642
pub linear_success_probability: bool,
643+
644+
/// In order to ensure we have knowledge for as many paths as possible, when probing it makes
645+
/// sense to bias away from channels for which we have very recent data.
646+
///
647+
/// This value is a penalty that is applied based on the last time that we updated the bounds
648+
/// on the available liquidity in a channel. The specified value is the maximum penalty that
649+
/// will be applied.
650+
///
651+
/// It obviously does not make sense to assign a non-0 value here unless you are using the
652+
/// pathfinding result for background probing.
653+
///
654+
/// Specifically, the following penalty is applied
655+
/// `probing_diversity_penalty_msat * max(0, (86400 - current time + last update))^2 / 86400^2` is
656+
///
657+
/// Default value: 0
658+
pub probing_diversity_penalty_msat: u64,
640659
}
641660

642661
impl Default for ProbabilisticScoringFeeParameters {
@@ -652,6 +671,7 @@ impl Default for ProbabilisticScoringFeeParameters {
652671
historical_liquidity_penalty_multiplier_msat: 10_000,
653672
historical_liquidity_penalty_amount_multiplier_msat: 64,
654673
linear_success_probability: false,
674+
probing_diversity_penalty_msat: 0,
655675
}
656676
}
657677
}
@@ -706,6 +726,7 @@ impl ProbabilisticScoringFeeParameters {
706726
anti_probing_penalty_msat: 0,
707727
considered_impossible_penalty_msat: 0,
708728
linear_success_probability: true,
729+
probing_diversity_penalty_msat: 0,
709730
}
710731
}
711732
}
@@ -850,6 +871,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ProbabilisticScorer<G, L> whe
850871
network_graph,
851872
logger,
852873
channel_liquidities: new_hash_map(),
874+
last_duration_since_epoch: Duration::from_secs(0),
853875
}
854876
}
855877

@@ -1172,7 +1194,7 @@ DirectedChannelLiquidity< L, HT, T> {
11721194
/// Returns a liquidity penalty for routing the given HTLC `amount_msat` through the channel in
11731195
/// this direction.
11741196
fn penalty_msat(
1175-
&self, amount_msat: u64, inflight_htlc_msat: u64,
1197+
&self, amount_msat: u64, inflight_htlc_msat: u64, last_duration_since_epoch: Duration,
11761198
score_params: &ProbabilisticScoringFeeParameters,
11771199
) -> u64 {
11781200
let total_inflight_amount_msat = amount_msat.saturating_add(inflight_htlc_msat);
@@ -1247,6 +1269,13 @@ DirectedChannelLiquidity< L, HT, T> {
12471269
}
12481270
}
12491271

1272+
if score_params.probing_diversity_penalty_msat != 0 {
1273+
let time_since_update = last_duration_since_epoch.saturating_sub(*self.last_datapoint);
1274+
let mul = Duration::from_secs(60 * 60 * 24).saturating_sub(time_since_update).as_secs();
1275+
let penalty = score_params.probing_diversity_penalty_msat.saturating_mul(mul * mul);
1276+
res = res.saturating_add(penalty / ((60 * 60 * 24) * (60 * 60 * 24)));
1277+
}
1278+
12501279
res
12511280
}
12521281

@@ -1398,11 +1427,12 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreLookUp for Probabilistic
13981427
}
13991428

14001429
let capacity_msat = usage.effective_capacity.as_msat();
1430+
let time = self.last_duration_since_epoch;
14011431
self.channel_liquidities
14021432
.get(scid)
14031433
.unwrap_or(&ChannelLiquidity::new(Duration::ZERO))
14041434
.as_directed(&source, &target, capacity_msat)
1405-
.penalty_msat(usage.amount_msat, usage.inflight_htlc_msat, score_params)
1435+
.penalty_msat(usage.amount_msat, usage.inflight_htlc_msat, time, score_params)
14061436
.saturating_add(anti_probing_penalty_msat)
14071437
.saturating_add(base_penalty_msat)
14081438
}
@@ -1448,6 +1478,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
14481478
}
14491479
if at_failed_channel { break; }
14501480
}
1481+
self.last_duration_since_epoch = duration_since_epoch;
14511482
}
14521483

14531484
fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
@@ -1475,6 +1506,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
14751506
hop.short_channel_id);
14761507
}
14771508
}
1509+
self.last_duration_since_epoch = duration_since_epoch;
14781510
}
14791511

14801512
fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
@@ -1506,6 +1538,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
15061538
liquidity.min_liquidity_offset_msat != 0 || liquidity.max_liquidity_offset_msat != 0 ||
15071539
liquidity.liquidity_history.has_datapoints()
15081540
});
1541+
self.last_duration_since_epoch = duration_since_epoch;
15091542
}
15101543
}
15111544

@@ -1939,15 +1972,20 @@ ReadableArgs<(ProbabilisticScoringDecayParameters, G, L)> for ProbabilisticScore
19391972
r: &mut R, args: (ProbabilisticScoringDecayParameters, G, L)
19401973
) -> Result<Self, DecodeError> {
19411974
let (decay_params, network_graph, logger) = args;
1942-
let mut channel_liquidities = new_hash_map();
1975+
let mut channel_liquidities: HashMap<u64, ChannelLiquidity> = new_hash_map();
19431976
read_tlv_fields!(r, {
19441977
(0, channel_liquidities, required),
19451978
});
1979+
let mut last_duration_since_epoch = Duration::from_secs(0);
1980+
for (_, liq) in channel_liquidities.iter() {
1981+
last_duration_since_epoch = cmp::max(last_duration_since_epoch, liq.last_updated);
1982+
}
19461983
Ok(Self {
19471984
decay_params,
19481985
network_graph,
19491986
logger,
19501987
channel_liquidities,
1988+
last_duration_since_epoch,
19511989
})
19521990
}
19531991
}
@@ -3542,6 +3580,52 @@ mod tests {
35423580
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params),
35433581
Some(0.0));
35443582
}
3583+
3584+
#[test]
3585+
fn probes_for_diversity() {
3586+
// Tests the probing_diversity_penalty_msat is applied
3587+
let logger = TestLogger::new();
3588+
let network_graph = network_graph(&logger);
3589+
let params = ProbabilisticScoringFeeParameters {
3590+
probing_diversity_penalty_msat: 1_000_000,
3591+
..ProbabilisticScoringFeeParameters::zero_penalty()
3592+
};
3593+
let decay_params = ProbabilisticScoringDecayParameters {
3594+
liquidity_offset_half_life: Duration::from_secs(10),
3595+
..ProbabilisticScoringDecayParameters::zero_penalty()
3596+
};
3597+
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
3598+
let source = source_node_id();
3599+
3600+
let usage = ChannelUsage {
3601+
amount_msat: 512,
3602+
inflight_htlc_msat: 0,
3603+
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
3604+
};
3605+
let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3606+
let (info, _) = channel.as_directed_from(&source).unwrap();
3607+
let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3608+
info,
3609+
short_channel_id: 42,
3610+
});
3611+
3612+
// Apply some update to set the last-update time to now
3613+
scorer.payment_path_failed(&payment_path_for_amount(1000), 42, Duration::ZERO);
3614+
3615+
// If no time has passed, we get the full probing_diversity_penalty_msat
3616+
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 1_000_000);
3617+
3618+
// As time passes the penalty decreases.
3619+
scorer.time_passed(Duration::from_secs(1));
3620+
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 999_976);
3621+
3622+
scorer.time_passed(Duration::from_secs(2));
3623+
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 999_953);
3624+
3625+
// Once we've gotten halfway through the day our penalty is 1/4 the configured value.
3626+
scorer.time_passed(Duration::from_secs(86400/2));
3627+
assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 250_000);
3628+
}
35453629
}
35463630

35473631
#[cfg(ldk_bench)]

0 commit comments

Comments
 (0)