Skip to content

Rewrite some documentation on ProbabilisticScorer and increase half-life to 6 hours. #1754

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 41 additions & 19 deletions lightning/src/routing/scoring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,26 +315,40 @@ type ConfiguredTime = Eternity;

/// [`Score`] implementation using channel success probability distributions.
///
/// Based on *Optimally Reliable & Cheap Payment Flows on the Lightning Network* by Rene Pickhardt
/// and Stefan Richter [[1]]. Given the uncertainty of channel liquidity balances, probability
/// distributions are defined based on knowledge learned from successful and unsuccessful attempts.
/// Then the negative `log10` of the success probability is used to determine the cost of routing a
/// specific HTLC amount through a channel.
/// Channels are tracked with upper and lower liquidity bounds - when an HTLC fails at a channel,
/// we learn that the upper-bound on the available liquidity is lower than the amount of the HTLC.
/// When a payment is forwarded through a channel (but fails later in the route), we learn the
/// lower-bound on the channel's available liquidity must be at least the value of the HTLC.
///
/// Knowledge about channel liquidity balances takes the form of upper and lower bounds on the
/// possible liquidity. Certainty of the bounds is decreased over time using a decay function. See
/// [`ProbabilisticScoringParameters`] for details.
/// These bounds are then used to determine a success probability using the formula from
/// *Optimally Reliable & Cheap Payment Flows on the Lightning Network* by Rene Pickhardt
/// and Stefan Richter [[1]] (i.e. `(upper_bound - payment_amount) / (upper_bound - lower_bound)`).
///
/// Since the scorer aims to learn the current channel liquidity balances, it works best for nodes
/// with high payment volume or that actively probe the [`NetworkGraph`]. Nodes with low payment
/// volume are more likely to experience failed payment paths, which would need to be retried.
/// This probability is combined with the [`liquidity_penalty_multiplier_msat`] and
/// [`liquidity_penalty_amount_multiplier_msat`] parameters to calculate a concrete penalty in
/// milli-satoshis. The penalties, when added across all hops, have the property of being linear in
/// terms of the entire path's success probability. This allows the router to directly compare
/// penalties for different paths. See the documentation of those parameters for the exact formulas.
///
/// The liquidity bounds are decayed by halving them every [`liquidity_offset_half_life`].
///
/// Further, we track the history of our upper and lower liquidity bounds for each channel,
/// allowing us to assign a second penalty (using [`historical_liquidity_penalty_multiplier_msat`]
/// and [`historical_liquidity_penalty_amount_multiplier_msat`]) based on the same probability
/// formula, but using the history of a channel rather than our latest estimates for the liquidity
/// bounds.
///
/// # Note
///
/// Mixing the `no-std` feature between serialization and deserialization results in undefined
/// behavior.
///
/// [1]: https://arxiv.org/abs/2107.05322
/// [`liquidity_penalty_multiplier_msat`]: ProbabilisticScoringParameters::liquidity_penalty_multiplier_msat
/// [`liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringParameters::liquidity_penalty_amount_multiplier_msat
/// [`liquidity_offset_half_life`]: ProbabilisticScoringParameters::liquidity_offset_half_life
/// [`historical_liquidity_penalty_multiplier_msat`]: ProbabilisticScoringParameters::historical_liquidity_penalty_multiplier_msat
/// [`historical_liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringParameters::historical_liquidity_penalty_amount_multiplier_msat
pub type ProbabilisticScorer<G, L> = ProbabilisticScorerUsingTime::<G, L, ConfiguredTime>;

/// Probabilistic [`Score`] implementation.
Expand Down Expand Up @@ -388,19 +402,27 @@ pub struct ProbabilisticScoringParameters {
/// uncertainty bounds of the channel liquidity balance. Amounts above the upper bound will
/// result in a `u64::max_value` penalty, however.
///
/// `-log10(success_probability) * liquidity_penalty_multiplier_msat`
///
/// Default value: 30,000 msat
///
/// [`liquidity_offset_half_life`]: Self::liquidity_offset_half_life
pub liquidity_penalty_multiplier_msat: u64,

/// The time required to elapse before any knowledge learned about channel liquidity balances is
/// cut in half.
/// Whenever this amount of time elapses since the last update to a channel's liquidity bounds,
/// the distance from the bounds to "zero" is cut in half. In other words, the lower-bound on
/// the available liquidity is halved and the upper-bound moves half-way to the channel's total
/// capacity.
///
/// Because halving the liquidity bounds grows the uncertainty on the channel's liquidity,
/// the penalty for an amount within the new bounds may change. See the [`ProbabilisticScorer`]
/// struct documentation for more info on the way the liquidity bounds are used.
///
/// The bounds are defined in terms of offsets and are initially zero. Increasing the offsets
/// gives tighter bounds on the channel liquidity balance. Thus, halving the offsets decreases
/// the certainty of the channel liquidity balance.
/// For example, if the channel's capacity is 1 million sats, and the current upper and lower
/// liquidity bounds are 200,000 sats and 600,000 sats, after this amount of time the upper
/// and lower liquidity bounds will be decayed to 100,000 and 800,000 sats.
///
/// Default value: 1 hour
/// Default value: 6 hours
///
/// # Note
///
Expand Down Expand Up @@ -758,7 +780,7 @@ impl ProbabilisticScoringParameters {
base_penalty_msat: 0,
base_penalty_amount_multiplier_msat: 0,
liquidity_penalty_multiplier_msat: 0,
liquidity_offset_half_life: Duration::from_secs(3600),
liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
liquidity_penalty_amount_multiplier_msat: 0,
historical_liquidity_penalty_multiplier_msat: 0,
historical_liquidity_penalty_amount_multiplier_msat: 0,
Expand All @@ -784,7 +806,7 @@ impl Default for ProbabilisticScoringParameters {
base_penalty_msat: 500,
base_penalty_amount_multiplier_msat: 8192,
liquidity_penalty_multiplier_msat: 30_000,
liquidity_offset_half_life: Duration::from_secs(3600),
liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
liquidity_penalty_amount_multiplier_msat: 192,
historical_liquidity_penalty_multiplier_msat: 10_000,
historical_liquidity_penalty_amount_multiplier_msat: 64,
Expand Down