Class ProbabilisticScorer


  • public class ProbabilisticScorer
    extends Object
    [`ScoreLookUp`] implementation using channel success probability distributions. 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. 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)`). 6762, 1070 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`]: ProbabilisticScoringFeeParameters::liquidity_penalty_multiplier_msat [`liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringFeeParameters::liquidity_penalty_amount_multiplier_msat [`liquidity_offset_half_life`]: ProbabilisticScoringDecayParameters::liquidity_offset_half_life [`historical_liquidity_penalty_multiplier_msat`]: ProbabilisticScoringFeeParameters::historical_liquidity_penalty_multiplier_msat [`historical_liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringFeeParameters::historical_liquidity_penalty_amount_multiplier_msat
    • Method Detail

      • debug_log_liquidity_stats

        public void debug_log_liquidity_stats()
        Dump the contents of this scorer into the configured logger. Note that this writes roughly one line per channel for which we have a liquidity estimate, which may be a substantial amount of log output.
      • estimated_channel_liquidity_range

        public Option_C2Tuple_u64u64ZZ estimated_channel_liquidity_range​(long scid,
                                                                         NodeId target)
        Query the estimated minimum and maximum liquidity available for sending a payment over the channel with `scid` towards the given `target` node.
      • historical_estimated_channel_liquidity_probabilities

        public Option_C2Tuple_ThirtyTwoU16sThirtyTwoU16sZZ historical_estimated_channel_liquidity_probabilities​(long scid,
                                                                                                                NodeId target)
        Query the historical estimated minimum and maximum liquidity available for sending a payment over the channel with `scid` towards the given `target` node. Returns two sets of 32 buckets. The first set describes the lower-bound liquidity history, the second set describes the upper-bound liquidity history. Each bucket describes the relative frequency at which we've seen a liquidity bound in the bucket's range relative to the channel's total capacity, on an arbitrary scale. Because the values are slowly decayed, more recent data points are weighted more heavily than older datapoints. Note that the range of each bucket varies by its location to provide more granular results at the edges of a channel's capacity, where it is more likely to sit. When scoring, the estimated probability that an upper-/lower-bound lies in a given bucket is calculated by dividing that bucket's value with the total value of all buckets. For example, using a lower bucket count for illustrative purposes, a value of `[0, 0, 0, ..., 0, 32]` indicates that we believe the probability of a bound being very close to the channel's capacity to be 100%, and have never (recently) seen it in any other bucket. A value of `[31, 0, 0, ..., 0, 0, 32]` indicates we've seen the bound being both in the top and bottom bucket, and roughly with similar (recent) frequency. Because the datapoints are decayed slowly over time, values will eventually return to `Some(([1; 32], [1; 32]))` and then to `None` once no datapoints remain. In order to fetch a single success probability from the buckets provided here, as used in the scoring model, see [`Self::historical_estimated_payment_success_probability`].
      • historical_estimated_payment_success_probability

        public Option_f64Z historical_estimated_payment_success_probability​(long scid,
                                                                            NodeId target,
                                                                            long amount_msat,
                                                                            ProbabilisticScoringFeeParameters params)
        Query the probability of payment success sending the given `amount_msat` over the channel with `scid` towards the given `target` node, based on the historical estimated liquidity bounds. These are the same bounds as returned by [`Self::historical_estimated_channel_liquidity_probabilities`] (but not those returned by [`Self::estimated_channel_liquidity_range`]).
      • as_ScoreLookUp

        public ScoreLookUp as_ScoreLookUp()
        Constructs a new ScoreLookUp which calls the relevant methods on this_arg. This copies the `inner` pointer in this_arg and thus the returned ScoreLookUp must be freed before this_arg is
      • as_ScoreUpdate

        public ScoreUpdate as_ScoreUpdate()
        Constructs a new ScoreUpdate which calls the relevant methods on this_arg. This copies the `inner` pointer in this_arg and thus the returned ScoreUpdate must be freed before this_arg is
      • as_Score

        public Score as_Score()
        Constructs a new Score which calls the relevant methods on this_arg. This copies the `inner` pointer in this_arg and thus the returned Score must be freed before this_arg is
      • write

        public byte[] write()
        Serialize the ProbabilisticScorer object into a byte array which can be read by ProbabilisticScorer_read