Skip to content

Commit

Permalink
Add probing_diversity_penalty_msat to the scorer parameters
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
TheBlueMatt committed Nov 23, 2024
1 parent 2d6720e commit bc8a318
Showing 1 changed file with 41 additions and 3 deletions.
44 changes: 41 additions & 3 deletions lightning/src/routing/scoring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,9 @@ where L::Target: Logger {
network_graph: G,
logger: L,
channel_liquidities: HashMap<u64, ChannelLiquidity>,
/// The last time we were given via a [`ScoreUpdate`] method. This does not imply that we've
/// decayed every liquidity bound up to that time.
last_duration_since_epoch: Duration,
}

/// Parameters for configuring [`ProbabilisticScorer`].
Expand Down Expand Up @@ -637,6 +640,22 @@ pub struct ProbabilisticScoringFeeParameters {
///
/// Default value: false
pub linear_success_probability: bool,

/// In order to ensure we have knowledge for as many paths as possible, when probing it makes
/// sense to bias away from channels for which we have very recent data.
///
/// This value is a penalty that is applied based on the last time that we updated the bounds
/// on the available liquidity in a channel. The specified value is the maximum penalty that
/// will be applied.
///
/// It obviously does not make sense to assign a non-0 value here unless you are using the
/// pathfinding result for background probing.
///
/// Specifically, the following penalty is applied
/// `probing_diversity_penalty_msat * max(0, (86400 - current time + last update))^2 / 86400^2` is
///
/// Default value: 0
pub probing_diversity_penalty_msat: u64,
}

impl Default for ProbabilisticScoringFeeParameters {
Expand All @@ -652,6 +671,7 @@ impl Default for ProbabilisticScoringFeeParameters {
historical_liquidity_penalty_multiplier_msat: 10_000,
historical_liquidity_penalty_amount_multiplier_msat: 64,
linear_success_probability: false,
probing_diversity_penalty_msat: 0,
}
}
}
Expand Down Expand Up @@ -706,6 +726,7 @@ impl ProbabilisticScoringFeeParameters {
anti_probing_penalty_msat: 0,
considered_impossible_penalty_msat: 0,
linear_success_probability: true,
probing_diversity_penalty_msat: 0,
}
}
}
Expand Down Expand Up @@ -823,6 +844,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ProbabilisticScorer<G, L> whe
network_graph,
logger,
channel_liquidities: new_hash_map(),
last_duration_since_epoch: Duration::from_secs(0),
}
}

Expand Down Expand Up @@ -1141,7 +1163,7 @@ DirectedChannelLiquidity< L, HT, T> {
/// Returns a liquidity penalty for routing the given HTLC `amount_msat` through the channel in
/// this direction.
fn penalty_msat(
&self, amount_msat: u64, inflight_htlc_msat: u64,
&self, amount_msat: u64, inflight_htlc_msat: u64, last_duration_since_epoch: Duration,
score_params: &ProbabilisticScoringFeeParameters,
) -> u64 {
let total_inflight_amount_msat = amount_msat.saturating_add(inflight_htlc_msat);
Expand Down Expand Up @@ -1216,6 +1238,13 @@ DirectedChannelLiquidity< L, HT, T> {
}
}

if score_params.probing_diversity_penalty_msat != 0 {
let time_since_update = last_duration_since_epoch.saturating_sub(*self.last_updated);
let mul = Duration::from_secs(60 * 60 * 24).saturating_sub(time_since_update).as_secs();
let penalty = score_params.probing_diversity_penalty_msat.saturating_mul(mul * mul);
res = res.saturating_add(penalty / ((60 * 60 * 24) * (60 * 60 * 24)));
}

res
}

Expand Down Expand Up @@ -1365,11 +1394,12 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreLookUp for Probabilistic
}

let capacity_msat = usage.effective_capacity.as_msat();
let time = self.last_duration_since_epoch;
self.channel_liquidities
.get(scid)
.unwrap_or(&ChannelLiquidity::new(Duration::ZERO))
.as_directed(&source, &target, capacity_msat)
.penalty_msat(usage.amount_msat, usage.inflight_htlc_msat, score_params)
.penalty_msat(usage.amount_msat, usage.inflight_htlc_msat, time, score_params)
.saturating_add(anti_probing_penalty_msat)
.saturating_add(base_penalty_msat)
}
Expand Down Expand Up @@ -1415,6 +1445,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
}
if at_failed_channel { break; }
}
self.last_duration_since_epoch = duration_since_epoch;
}

fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
Expand Down Expand Up @@ -1442,6 +1473,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
hop.short_channel_id);
}
}
self.last_duration_since_epoch = duration_since_epoch;
}

fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
Expand Down Expand Up @@ -1473,6 +1505,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for Probabilistic
liquidity.min_liquidity_offset_msat != 0 || liquidity.max_liquidity_offset_msat != 0 ||
liquidity.liquidity_history.has_datapoints()
});
self.last_duration_since_epoch = duration_since_epoch;
}
}

Expand Down Expand Up @@ -1906,15 +1939,20 @@ ReadableArgs<(ProbabilisticScoringDecayParameters, G, L)> for ProbabilisticScore
r: &mut R, args: (ProbabilisticScoringDecayParameters, G, L)
) -> Result<Self, DecodeError> {
let (decay_params, network_graph, logger) = args;
let mut channel_liquidities = new_hash_map();
let mut channel_liquidities: HashMap<u64, ChannelLiquidity> = new_hash_map();
read_tlv_fields!(r, {
(0, channel_liquidities, required),
});
let mut last_duration_since_epoch = Duration::from_secs(0);
for (_, liq) in channel_liquidities.iter() {
last_duration_since_epoch = cmp::max(last_duration_since_epoch, liq.last_updated);
}
Ok(Self {
decay_params,
network_graph,
logger,
channel_liquidities,
last_duration_since_epoch,
})
}
}
Expand Down

0 comments on commit bc8a318

Please sign in to comment.