+ Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
+ params.liquidity_penalty_multiplier_msat,
+ params.liquidity_penalty_amount_multiplier_msat)
+ }
+ };
+
+ if params.historical_liquidity_penalty_multiplier_msat != 0 ||
+ params.historical_liquidity_penalty_amount_multiplier_msat != 0 {
+ // If historical penalties are enabled, calculate the penalty by walking the set of
+ // historical liquidity bucket (min, max) combinations (where min_idx < max_idx)
+ // and, for each, calculate the probability of success given our payment amount, then
+ // total the weighted average probability of success.
+ //
+ // We use a sliding scale to decide which point within a given bucket will be compared
+ // to the amount being sent - for lower-bounds, the amount being sent is compared to
+ // the lower edge of the first bucket (i.e. zero), but compared to the upper 7/8ths of
+ // the last bucket (i.e. 9 times the index, or 63), with each bucket in between
+ // increasing the comparison point by 1/64th. For upper-bounds, the same applies,
+ // however with an offset of 1/64th (i.e. starting at one and ending at 64). This
+ // avoids failing to assign penalties to channels at the edges.
+ //
+ // If we used the bottom edge of buckets, we'd end up never assigning any penalty at
+ // all to such a channel when sending less than ~0.19% of the channel's capacity (e.g.
+ // ~200k sats for a 1 BTC channel!).
+ //
+ // If we used the middle of each bucket we'd never assign any penalty at all when
+ // sending less than 1/16th of a channel's capacity, or 1/8th if we used the top of the
+ // bucket.
+ let mut total_valid_points_tracked = 0;
+ for (min_idx, min_bucket) in self.min_liquidity_offset_history.buckets.iter().enumerate() {
+ for max_bucket in self.max_liquidity_offset_history.buckets.iter().take(8 - min_idx) {
+ total_valid_points_tracked += (*min_bucket as u64) * (*max_bucket as u64);
+ }
+ }
+ if total_valid_points_tracked == 0 {
+ // If we don't have any valid points, redo the non-historical calculation with no
+ // liquidity bounds tracked and the historical penalty multipliers.
+ let max_capacity = self.capacity_msat.saturating_sub(amount_msat).saturating_add(1);
+ let negative_log10_times_2048 =
+ approx::negative_log10_times_2048(max_capacity, self.capacity_msat.saturating_add(1));
+ res = res.saturating_add(Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
+ params.historical_liquidity_penalty_multiplier_msat,
+ params.historical_liquidity_penalty_amount_multiplier_msat));
+ return res;