lightning/routing/
scoring.rs

1// This file is Copyright its original authors, visible in version control
2// history.
3//
4// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7// You may not use this file except in accordance with one or both of these
8// licenses.
9
10//! Utilities for scoring payment channels.
11//!
12//! [`ProbabilisticScorer`] may be given to [`find_route`] to score payment channels during path
13//! finding when a custom [`ScoreLookUp`] implementation is not needed.
14//!
15//! # Example
16//!
17//! ```
18//! # extern crate bitcoin;
19//! #
20//! # use lightning::routing::gossip::NetworkGraph;
21//! # use lightning::routing::router::{RouteParameters, find_route};
22//! # use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters};
23//! # use lightning::sign::KeysManager;
24//! # use lightning::util::logger::{Logger, Record};
25//! # use bitcoin::secp256k1::PublicKey;
26//! #
27//! # struct FakeLogger {};
28//! # impl Logger for FakeLogger {
29//! #     fn log(&self, record: Record) { unimplemented!() }
30//! # }
31//! # fn find_scored_route(payer: PublicKey, route_params: RouteParameters, network_graph: NetworkGraph<&FakeLogger>) {
32//! # let logger = FakeLogger {};
33//! #
34//! // Use the default channel penalties.
35//! let params = ProbabilisticScoringFeeParameters::default();
36//! let decay_params = ProbabilisticScoringDecayParameters::default();
37//! let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
38//!
39//! // Or use custom channel penalties.
40//! let params = ProbabilisticScoringFeeParameters {
41//! 	liquidity_penalty_multiplier_msat: 2 * 1000,
42//! 	..ProbabilisticScoringFeeParameters::default()
43//! };
44//! let decay_params = ProbabilisticScoringDecayParameters::default();
45//! let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
46//! # let random_seed_bytes = [42u8; 32];
47//!
48//! let route = find_route(&payer, &route_params, &network_graph, None, &logger, &scorer, &params, &random_seed_bytes);
49//! # }
50//! ```
51//!
52//! [`find_route`]: crate::routing::router::find_route
53
54use crate::io::{self, Read};
55use crate::ln::msgs::DecodeError;
56use crate::prelude::hash_map::Entry;
57use crate::prelude::*;
58use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, NetworkGraph, NodeId};
59use crate::routing::log_approx;
60use crate::routing::router::{CandidateRouteHop, Path, PublicHopCandidate};
61use crate::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
62use crate::util::logger::Logger;
63use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
64use bucketed_history::{
65	DirectedHistoricalLiquidityTracker, HistoricalBucketRangeTracker, HistoricalLiquidityTracker,
66	LegacyHistoricalBucketRangeTracker,
67};
68use core::ops::{Deref, DerefMut};
69use core::time::Duration;
70use core::{cmp, fmt, mem};
71#[cfg(not(c_bindings))]
72use {
73	crate::sync::{Mutex, MutexGuard},
74	core::cell::{Ref, RefCell, RefMut},
75};
76
77/// We define Score ever-so-slightly differently based on whether we are being built for C bindings
78/// or not. For users, `LockableScore` must somehow be writeable to disk. For Rust users, this is
79/// no problem - you move a `Score` that implements `Writeable` into a `Mutex`, lock it, and now
80/// you have the original, concrete, `Score` type, which presumably implements `Writeable`.
81///
82/// For C users, once you've moved the `Score` into a `LockableScore` all you have after locking it
83/// is an opaque trait object with an opaque pointer with no type info. Users could take the unsafe
84/// approach of blindly casting that opaque pointer to a concrete type and calling `Writeable` from
85/// there, but other languages downstream of the C bindings (e.g. Java) can't even do that.
86/// Instead, we really want `Score` and `LockableScore` to implement `Writeable` directly, which we
87/// do here by defining `Score` differently for `cfg(c_bindings)`.
88macro_rules! define_score { ($($supertrait: path)*) => {
89/// An interface used to score payment channels for path finding.
90///
91/// `ScoreLookUp` is used to determine the penalty for a given channel.
92///
93/// Scoring is in terms of fees willing to be paid in order to avoid routing through a channel.
94pub trait ScoreLookUp {
95	/// A configurable type which should contain various passed-in parameters for configuring the scorer,
96	/// on a per-routefinding-call basis through to the scorer methods,
97	/// which are used to determine the parameters for the suitability of channels for use.
98	type ScoreParams;
99	/// Returns the fee in msats willing to be paid to avoid routing `send_amt_msat` through the
100	/// given channel in the direction from `source` to `target`.
101	///
102	/// The channel's capacity (less any other MPP parts that are also being considered for use in
103	/// the same payment) is given by `capacity_msat`. It may be determined from various sources
104	/// such as a chain data, network gossip, or invoice hints. For invoice hints, a capacity near
105	/// [`u64::max_value`] is given to indicate sufficient capacity for the invoice's full amount.
106	/// Thus, implementations should be overflow-safe.
107	fn channel_penalty_msat(
108		&self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams
109	) -> u64;
110}
111
112/// `ScoreUpdate` is used to update the scorer's internal state after a payment attempt.
113pub trait ScoreUpdate {
114	/// Handles updating channel penalties after failing to route through a channel.
115	fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration);
116
117	/// Handles updating channel penalties after successfully routing along a path.
118	fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration);
119
120	/// Handles updating channel penalties after a probe over the given path failed.
121	fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration);
122
123	/// Handles updating channel penalties after a probe over the given path succeeded.
124	fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration);
125
126	/// Scorers may wish to reduce their certainty of channel liquidity information over time.
127	/// Thus, this method is provided to allow scorers to observe the passage of time - the holder
128	/// of this object should call this method regularly (generally via the
129	/// `lightning-background-processor` crate).
130	fn time_passed(&mut self, duration_since_epoch: Duration);
131}
132
133/// A trait which can both lookup and update routing channel penalty scores.
134///
135/// This is used in places where both bounds are required and implemented for all types which
136/// implement [`ScoreLookUp`] and [`ScoreUpdate`].
137///
138/// Bindings users may need to manually implement this for their custom scoring implementations.
139pub trait Score : ScoreLookUp + ScoreUpdate $(+ $supertrait)* {}
140
141#[cfg(not(c_bindings))]
142impl<T: ScoreLookUp + ScoreUpdate $(+ $supertrait)*> Score for T {}
143
144#[cfg(not(c_bindings))]
145impl<S: ScoreLookUp, T: Deref<Target=S>> ScoreLookUp for T {
146	type ScoreParams = S::ScoreParams;
147	fn channel_penalty_msat(
148		&self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams
149	) -> u64 {
150		self.deref().channel_penalty_msat(candidate, usage, score_params)
151	}
152}
153
154#[cfg(not(c_bindings))]
155impl<S: ScoreUpdate, T: DerefMut<Target=S>> ScoreUpdate for T {
156	fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
157		self.deref_mut().payment_path_failed(path, short_channel_id, duration_since_epoch)
158	}
159
160	fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
161		self.deref_mut().payment_path_successful(path, duration_since_epoch)
162	}
163
164	fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
165		self.deref_mut().probe_failed(path, short_channel_id, duration_since_epoch)
166	}
167
168	fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
169		self.deref_mut().probe_successful(path, duration_since_epoch)
170	}
171
172	fn time_passed(&mut self, duration_since_epoch: Duration) {
173		self.deref_mut().time_passed(duration_since_epoch)
174	}
175}
176} }
177
178#[cfg(c_bindings)]
179define_score!(Writeable);
180
181#[cfg(not(c_bindings))]
182define_score!();
183
184/// A scorer that is accessed under a lock.
185///
186/// Needed so that calls to [`ScoreLookUp::channel_penalty_msat`] in [`find_route`] can be made while
187/// having shared ownership of a scorer but without requiring internal locking in [`ScoreUpdate`]
188/// implementations. Internal locking would be detrimental to route finding performance and could
189/// result in [`ScoreLookUp::channel_penalty_msat`] returning a different value for the same channel.
190///
191/// [`find_route`]: crate::routing::router::find_route
192pub trait LockableScore<'a> {
193	/// The [`ScoreUpdate`] type.
194	type ScoreUpdate: 'a + ScoreUpdate;
195	/// The [`ScoreLookUp`] type.
196	type ScoreLookUp: 'a + ScoreLookUp;
197
198	/// The write locked [`ScoreUpdate`] type.
199	type WriteLocked: DerefMut<Target = Self::ScoreUpdate> + Sized;
200
201	/// The read locked [`ScoreLookUp`] type.
202	type ReadLocked: Deref<Target = Self::ScoreLookUp> + Sized;
203
204	/// Returns read locked scorer.
205	fn read_lock(&'a self) -> Self::ReadLocked;
206
207	/// Returns write locked scorer.
208	fn write_lock(&'a self) -> Self::WriteLocked;
209}
210
211/// Refers to a scorer that is accessible under lock and also writeable to disk
212///
213/// We need this trait to be able to pass in a scorer to `lightning-background-processor` that will enable us to
214/// use the Persister to persist it.
215pub trait WriteableScore<'a>: LockableScore<'a> + Writeable {}
216
217#[cfg(not(c_bindings))]
218impl<'a, T> WriteableScore<'a> for T where T: LockableScore<'a> + Writeable {}
219#[cfg(not(c_bindings))]
220impl<'a, T: Score + 'a> LockableScore<'a> for Mutex<T> {
221	type ScoreUpdate = T;
222	type ScoreLookUp = T;
223
224	type WriteLocked = MutexGuard<'a, Self::ScoreUpdate>;
225	type ReadLocked = MutexGuard<'a, Self::ScoreLookUp>;
226
227	fn read_lock(&'a self) -> Self::ReadLocked {
228		Mutex::lock(self).unwrap()
229	}
230
231	fn write_lock(&'a self) -> Self::WriteLocked {
232		Mutex::lock(self).unwrap()
233	}
234}
235
236#[cfg(not(c_bindings))]
237impl<'a, T: Score + 'a> LockableScore<'a> for RefCell<T> {
238	type ScoreUpdate = T;
239	type ScoreLookUp = T;
240
241	type WriteLocked = RefMut<'a, Self::ScoreUpdate>;
242	type ReadLocked = Ref<'a, Self::ScoreLookUp>;
243
244	fn write_lock(&'a self) -> Self::WriteLocked {
245		self.borrow_mut()
246	}
247
248	fn read_lock(&'a self) -> Self::ReadLocked {
249		self.borrow()
250	}
251}
252
253#[cfg(any(not(c_bindings), feature = "_test_utils", test))]
254impl<'a, T: Score + 'a> LockableScore<'a> for RwLock<T> {
255	type ScoreUpdate = T;
256	type ScoreLookUp = T;
257
258	type WriteLocked = RwLockWriteGuard<'a, Self::ScoreLookUp>;
259	type ReadLocked = RwLockReadGuard<'a, Self::ScoreUpdate>;
260
261	fn read_lock(&'a self) -> Self::ReadLocked {
262		RwLock::read(self).unwrap()
263	}
264
265	fn write_lock(&'a self) -> Self::WriteLocked {
266		RwLock::write(self).unwrap()
267	}
268}
269
270#[cfg(c_bindings)]
271/// A concrete implementation of [`LockableScore`] which supports multi-threading.
272pub struct MultiThreadedLockableScore<T: Score> {
273	score: RwLock<T>,
274}
275
276#[cfg(c_bindings)]
277impl<'a, T: Score + 'a> LockableScore<'a> for MultiThreadedLockableScore<T> {
278	type ScoreUpdate = T;
279	type ScoreLookUp = T;
280	type WriteLocked = MultiThreadedScoreLockWrite<'a, Self::ScoreUpdate>;
281	type ReadLocked = MultiThreadedScoreLockRead<'a, Self::ScoreLookUp>;
282
283	fn read_lock(&'a self) -> Self::ReadLocked {
284		MultiThreadedScoreLockRead(self.score.read().unwrap())
285	}
286
287	fn write_lock(&'a self) -> Self::WriteLocked {
288		MultiThreadedScoreLockWrite(self.score.write().unwrap())
289	}
290}
291
292#[cfg(c_bindings)]
293impl<T: Score> Writeable for MultiThreadedLockableScore<T> {
294	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
295		self.score.read().unwrap().write(writer)
296	}
297}
298
299#[cfg(c_bindings)]
300impl<'a, T: Score + 'a> WriteableScore<'a> for MultiThreadedLockableScore<T> {}
301
302#[cfg(c_bindings)]
303impl<T: Score> MultiThreadedLockableScore<T> {
304	/// Creates a new [`MultiThreadedLockableScore`] given an underlying [`Score`].
305	pub fn new(score: T) -> Self {
306		MultiThreadedLockableScore { score: RwLock::new(score) }
307	}
308}
309
310#[cfg(c_bindings)]
311/// A locked `MultiThreadedLockableScore`.
312pub struct MultiThreadedScoreLockRead<'a, T: Score>(RwLockReadGuard<'a, T>);
313
314#[cfg(c_bindings)]
315/// A locked `MultiThreadedLockableScore`.
316pub struct MultiThreadedScoreLockWrite<'a, T: Score>(RwLockWriteGuard<'a, T>);
317
318#[cfg(c_bindings)]
319impl<'a, T: 'a + Score> Deref for MultiThreadedScoreLockRead<'a, T> {
320	type Target = T;
321
322	fn deref(&self) -> &Self::Target {
323		self.0.deref()
324	}
325}
326
327#[cfg(c_bindings)]
328impl<'a, T: Score> ScoreLookUp for MultiThreadedScoreLockRead<'a, T> {
329	type ScoreParams = T::ScoreParams;
330	fn channel_penalty_msat(
331		&self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams,
332	) -> u64 {
333		self.0.channel_penalty_msat(candidate, usage, score_params)
334	}
335}
336
337#[cfg(c_bindings)]
338impl<'a, T: Score> Writeable for MultiThreadedScoreLockWrite<'a, T> {
339	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
340		self.0.write(writer)
341	}
342}
343
344#[cfg(c_bindings)]
345impl<'a, T: 'a + Score> Deref for MultiThreadedScoreLockWrite<'a, T> {
346	type Target = T;
347
348	fn deref(&self) -> &Self::Target {
349		self.0.deref()
350	}
351}
352
353#[cfg(c_bindings)]
354impl<'a, T: 'a + Score> DerefMut for MultiThreadedScoreLockWrite<'a, T> {
355	fn deref_mut(&mut self) -> &mut Self::Target {
356		self.0.deref_mut()
357	}
358}
359
360#[cfg(c_bindings)]
361impl<'a, T: Score> ScoreUpdate for MultiThreadedScoreLockWrite<'a, T> {
362	fn payment_path_failed(
363		&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration,
364	) {
365		self.0.payment_path_failed(path, short_channel_id, duration_since_epoch)
366	}
367
368	fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
369		self.0.payment_path_successful(path, duration_since_epoch)
370	}
371
372	fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
373		self.0.probe_failed(path, short_channel_id, duration_since_epoch)
374	}
375
376	fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
377		self.0.probe_successful(path, duration_since_epoch)
378	}
379
380	fn time_passed(&mut self, duration_since_epoch: Duration) {
381		self.0.time_passed(duration_since_epoch)
382	}
383}
384
385/// Proposed use of a channel passed as a parameter to [`ScoreLookUp::channel_penalty_msat`].
386#[derive(Clone, Copy, Debug, PartialEq)]
387pub struct ChannelUsage {
388	/// The amount to send through the channel, denominated in millisatoshis.
389	pub amount_msat: u64,
390
391	/// Total amount, denominated in millisatoshis, already allocated to send through the channel
392	/// as part of a multi-path payment.
393	pub inflight_htlc_msat: u64,
394
395	/// The effective capacity of the channel.
396	pub effective_capacity: EffectiveCapacity,
397}
398
399#[derive(Clone)]
400/// [`ScoreLookUp`] implementation that uses a fixed penalty.
401pub struct FixedPenaltyScorer {
402	penalty_msat: u64,
403}
404
405impl FixedPenaltyScorer {
406	/// Creates a new scorer using `penalty_msat`.
407	pub fn with_penalty(penalty_msat: u64) -> Self {
408		Self { penalty_msat }
409	}
410}
411
412impl ScoreLookUp for FixedPenaltyScorer {
413	type ScoreParams = ();
414	fn channel_penalty_msat(
415		&self, _: &CandidateRouteHop, _: ChannelUsage, _score_params: &Self::ScoreParams,
416	) -> u64 {
417		self.penalty_msat
418	}
419}
420
421impl ScoreUpdate for FixedPenaltyScorer {
422	#[rustfmt::skip]
423	fn payment_path_failed(&mut self, _path: &Path, _short_channel_id: u64, _duration_since_epoch: Duration) {}
424
425	fn payment_path_successful(&mut self, _path: &Path, _duration_since_epoch: Duration) {}
426
427	#[rustfmt::skip]
428	fn probe_failed(&mut self, _path: &Path, _short_channel_id: u64, _duration_since_epoch: Duration) {}
429
430	fn probe_successful(&mut self, _path: &Path, _duration_since_epoch: Duration) {}
431
432	fn time_passed(&mut self, _duration_since_epoch: Duration) {}
433}
434
435impl Writeable for FixedPenaltyScorer {
436	#[inline]
437	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
438		write_tlv_fields!(w, {});
439		Ok(())
440	}
441}
442
443impl ReadableArgs<u64> for FixedPenaltyScorer {
444	#[inline]
445	fn read<R: Read>(r: &mut R, penalty_msat: u64) -> Result<Self, DecodeError> {
446		read_tlv_fields!(r, {});
447		Ok(Self { penalty_msat })
448	}
449}
450
451/// [`ScoreLookUp`] implementation using channel success probability distributions.
452///
453/// Channels are tracked with upper and lower liquidity bounds - when an HTLC fails at a channel,
454/// we learn that the upper-bound on the available liquidity is lower than the amount of the HTLC.
455/// When a payment is forwarded through a channel (but fails later in the route), we learn the
456/// lower-bound on the channel's available liquidity must be at least the value of the HTLC.
457///
458/// These bounds are then used to determine a success probability using the formula from
459/// *Optimally Reliable & Cheap Payment Flows on the Lightning Network* by Rene Pickhardt
460/// and Stefan Richter [[1]] (i.e. `(upper_bound - payment_amount) / (upper_bound - lower_bound)`).
461///
462/// This probability is combined with the [`liquidity_penalty_multiplier_msat`] and
463/// [`liquidity_penalty_amount_multiplier_msat`] parameters to calculate a concrete penalty in
464/// milli-satoshis. The penalties, when added across all hops, have the property of being linear in
465/// terms of the entire path's success probability. This allows the router to directly compare
466/// penalties for different paths. See the documentation of those parameters for the exact formulas.
467///
468/// The liquidity bounds are decayed by halving them every [`liquidity_offset_half_life`].
469///
470/// Further, we track the history of our upper and lower liquidity bounds for each channel,
471/// allowing us to assign a second penalty (using [`historical_liquidity_penalty_multiplier_msat`]
472/// and [`historical_liquidity_penalty_amount_multiplier_msat`]) based on the same probability
473/// formula, but using the history of a channel rather than our latest estimates for the liquidity
474/// bounds.
475///
476/// [1]: https://arxiv.org/abs/2107.05322
477/// [`liquidity_penalty_multiplier_msat`]: ProbabilisticScoringFeeParameters::liquidity_penalty_multiplier_msat
478/// [`liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringFeeParameters::liquidity_penalty_amount_multiplier_msat
479/// [`liquidity_offset_half_life`]: ProbabilisticScoringDecayParameters::liquidity_offset_half_life
480/// [`historical_liquidity_penalty_multiplier_msat`]: ProbabilisticScoringFeeParameters::historical_liquidity_penalty_multiplier_msat
481/// [`historical_liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringFeeParameters::historical_liquidity_penalty_amount_multiplier_msat
482pub struct ProbabilisticScorer<G: Deref<Target = NetworkGraph<L>>, L: Deref>
483where
484	L::Target: Logger,
485{
486	decay_params: ProbabilisticScoringDecayParameters,
487	network_graph: G,
488	logger: L,
489	channel_liquidities: ChannelLiquidities,
490	/// The last time we were given via a [`ScoreUpdate`] method. This does not imply that we've
491	/// decayed every liquidity bound up to that time.
492	last_update_time: Duration,
493}
494/// Container for live and historical liquidity bounds for each channel.
495#[derive(Clone)]
496pub struct ChannelLiquidities(HashMap<u64, ChannelLiquidity>);
497
498impl ChannelLiquidities {
499	fn new() -> Self {
500		Self(new_hash_map())
501	}
502
503	#[rustfmt::skip]
504	fn time_passed(&mut self, duration_since_epoch: Duration, decay_params: ProbabilisticScoringDecayParameters) {
505		self.0.retain(|_scid, liquidity| {
506			liquidity.min_liquidity_offset_msat =
507				liquidity.decayed_offset(liquidity.min_liquidity_offset_msat, duration_since_epoch, decay_params);
508			liquidity.max_liquidity_offset_msat =
509				liquidity.decayed_offset(liquidity.max_liquidity_offset_msat, duration_since_epoch, decay_params);
510			liquidity.last_updated = duration_since_epoch;
511
512			// Only decay the historical buckets if there hasn't been new data for a while. This ties back to our
513			// earlier conclusion that fixed half-lives for scoring data are inherently flawed—they tend to be either
514			// too fast or too slow. Ideally, historical buckets should only decay as new data is added, which naturally
515			// happens when fresh data arrives. However, scoring a channel based on month-old data while treating it the
516			// same as one with minute-old data is problematic. To address this, we introduced a decay mechanism, but it
517			// runs very slowly and only activates when no new data has been received for a while, as our preference is
518			// to decay based on incoming data.
519			let elapsed_time =
520				duration_since_epoch.saturating_sub(liquidity.offset_history_last_updated);
521			if elapsed_time > decay_params.historical_no_updates_half_life {
522				let half_life = decay_params.historical_no_updates_half_life.as_secs_f64();
523				if half_life != 0.0 {
524					liquidity.liquidity_history.decay_buckets(elapsed_time.as_secs_f64() / half_life);
525					liquidity.offset_history_last_updated = duration_since_epoch;
526				}
527			}
528			liquidity.min_liquidity_offset_msat != 0 || liquidity.max_liquidity_offset_msat != 0 ||
529				liquidity.liquidity_history.has_datapoints()
530		});
531	}
532
533	fn get(&self, short_channel_id: &u64) -> Option<&ChannelLiquidity> {
534		self.0.get(short_channel_id)
535	}
536
537	fn insert(
538		&mut self, short_channel_id: u64, liquidity: ChannelLiquidity,
539	) -> Option<ChannelLiquidity> {
540		self.0.insert(short_channel_id, liquidity)
541	}
542
543	fn iter(&self) -> impl Iterator<Item = (&u64, &ChannelLiquidity)> {
544		self.0.iter()
545	}
546
547	fn entry(&mut self, short_channel_id: u64) -> Entry<'_, u64, ChannelLiquidity, RandomState> {
548		self.0.entry(short_channel_id)
549	}
550
551	#[cfg(test)]
552	fn get_mut(&mut self, short_channel_id: &u64) -> Option<&mut ChannelLiquidity> {
553		self.0.get_mut(short_channel_id)
554	}
555}
556
557impl Readable for ChannelLiquidities {
558	#[inline]
559	fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
560		let mut channel_liquidities = new_hash_map();
561		read_tlv_fields!(r, {
562			(0, channel_liquidities, required),
563		});
564		Ok(ChannelLiquidities(channel_liquidities))
565	}
566}
567
568impl Writeable for ChannelLiquidities {
569	#[inline]
570	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
571		write_tlv_fields!(w, {
572			(0, self.0, required),
573		});
574		Ok(())
575	}
576}
577
578/// Parameters for configuring [`ProbabilisticScorer`].
579///
580/// Used to configure base, liquidity, and amount penalties, the sum of which comprises the channel
581/// penalty (i.e., the amount in msats willing to be paid to avoid routing through the channel).
582///
583/// The penalty applied to any channel by the [`ProbabilisticScorer`] is the sum of each of the
584/// parameters here.
585#[derive(Clone, Debug)]
586pub struct ProbabilisticScoringFeeParameters {
587	/// A fixed penalty in msats to apply to each channel.
588	///
589	/// In testing, a value of roughly 1/10th of [`historical_liquidity_penalty_multiplier_msat`]
590	/// (implying scaling all estimated probabilities down by a factor of ~79%) resulted in the
591	/// most accurate total success probabilities.
592	///
593	/// Default value: 1,024 msat (i.e. we're willing to pay 1 sat to avoid each additional hop).
594	///
595	/// [`historical_liquidity_penalty_multiplier_msat`]: Self::historical_liquidity_penalty_multiplier_msat
596	pub base_penalty_msat: u64,
597
598	/// A multiplier used with the payment amount to calculate a fixed penalty applied to each
599	/// channel, in excess of the [`base_penalty_msat`].
600	///
601	/// The purpose of the amount penalty is to avoid having fees dominate the channel cost (i.e.,
602	/// fees plus penalty) for large payments. The penalty is computed as the product of this
603	/// multiplier and `2^30`ths of the payment amount.
604	///
605	/// ie `base_penalty_amount_multiplier_msat * amount_msat / 2^30`
606	///
607	/// In testing, a value of roughly ~100x (1/10th * 2^10) of
608	/// [`historical_liquidity_penalty_amount_multiplier_msat`] (implying scaling all estimated
609	/// probabilities down by a factor of ~79%) resulted in the most accurate total success
610	/// probabilities.
611	///
612	/// Default value: 131,072 msat (i.e. we're willing to pay 0.125bps to avoid each additional
613	///                              hop).
614	///
615	/// [`base_penalty_msat`]: Self::base_penalty_msat
616	/// [`historical_liquidity_penalty_amount_multiplier_msat`]: Self::historical_liquidity_penalty_amount_multiplier_msat
617	pub base_penalty_amount_multiplier_msat: u64,
618
619	/// A multiplier used in conjunction with the negative `log10` of the channel's success
620	/// probability for a payment, as determined by our latest estimates of the channel's
621	/// liquidity, to determine the liquidity penalty.
622	///
623	/// The penalty is based in part on the knowledge learned from prior successful and unsuccessful
624	/// payments. This knowledge is decayed over time based on [`liquidity_offset_half_life`]. The
625	/// penalty is effectively limited to `2 * liquidity_penalty_multiplier_msat` (corresponding to
626	/// lower bounding the success probability to `0.01`) when the amount falls within the
627	/// uncertainty bounds of the channel liquidity balance. Amounts above the upper bound will
628	/// result in a `u64::max_value` penalty, however.
629	///
630	/// `-log10(success_probability) * liquidity_penalty_multiplier_msat`
631	///
632	/// In testing, this scoring model performs much worse than the historical scoring model
633	/// configured with the [`historical_liquidity_penalty_multiplier_msat`] and thus is disabled
634	/// by default.
635	///
636	/// Default value: 0 msat
637	///
638	/// [`liquidity_offset_half_life`]: ProbabilisticScoringDecayParameters::liquidity_offset_half_life
639	/// [`historical_liquidity_penalty_multiplier_msat`]: Self::historical_liquidity_penalty_multiplier_msat
640	pub liquidity_penalty_multiplier_msat: u64,
641
642	/// A multiplier used in conjunction with the payment amount and the negative `log10` of the
643	/// channel's success probability for the total amount flowing over a channel, as determined by
644	/// our latest estimates of the channel's liquidity, to determine the amount penalty.
645	///
646	/// The purpose of the amount penalty is to avoid having fees dominate the channel cost (i.e.,
647	/// fees plus penalty) for large payments. The penalty is computed as the product of this
648	/// multiplier and `2^20`ths of the payment amount, weighted by the negative `log10` of the
649	/// success probability.
650	///
651	/// `-log10(success_probability) * liquidity_penalty_amount_multiplier_msat * amount_msat / 2^20`
652	///
653	/// In practice, this means for 0.1 success probability (`-log10(0.1) == 1`) each `2^20`th of
654	/// the amount will result in a penalty of the multiplier. And, as the success probability
655	/// decreases, the negative `log10` weighting will increase dramatically. For higher success
656	/// probabilities, the multiplier will have a decreasing effect as the negative `log10` will
657	/// fall below `1`.
658	///
659	/// In testing, this scoring model performs much worse than the historical scoring model
660	/// configured with the [`historical_liquidity_penalty_amount_multiplier_msat`] and thus is
661	/// disabled by default.
662	///
663	/// Default value: 0 msat
664	///
665	/// [`historical_liquidity_penalty_amount_multiplier_msat`]: Self::historical_liquidity_penalty_amount_multiplier_msat
666	pub liquidity_penalty_amount_multiplier_msat: u64,
667
668	/// A multiplier used in conjunction with the negative `log10` of the channel's success
669	/// probability for the payment, as determined based on the history of our estimates of the
670	/// channel's available liquidity, to determine a penalty.
671	///
672	/// This penalty is similar to [`liquidity_penalty_multiplier_msat`], however, instead of using
673	/// only our latest estimate for the current liquidity available in the channel, it estimates
674	/// success probability based on the estimated liquidity available in the channel through
675	/// history. Specifically, every time we update our liquidity bounds on a given channel, we
676	/// track which of several buckets those bounds fall into, exponentially decaying the
677	/// probability of each bucket as new samples are added.
678	///
679	/// Default value: 10,000 msat (i.e. willing to pay 1 sat to avoid an 80% probability channel,
680	///                            or 6 sats to avoid a 25% probability channel).
681	///
682	/// [`liquidity_penalty_multiplier_msat`]: Self::liquidity_penalty_multiplier_msat
683	pub historical_liquidity_penalty_multiplier_msat: u64,
684
685	/// A multiplier used in conjunction with the payment amount and the negative `log10` of the
686	/// channel's success probability for the total amount flowing over a channel, as determined
687	/// based on the history of our estimates of the channel's available liquidity, to determine a
688	/// penalty.
689	///
690	/// The purpose of the amount penalty is to avoid having fees dominate the channel cost for
691	/// large payments. The penalty is computed as the product of this multiplier and `2^20`ths
692	/// of the payment amount, weighted by the negative `log10` of the success probability.
693	///
694	/// This penalty is similar to [`liquidity_penalty_amount_multiplier_msat`], however, instead
695	/// of using only our latest estimate for the current liquidity available in the channel, it
696	/// estimates success probability based on the estimated liquidity available in the channel
697	/// through history. Specifically, every time we update our liquidity bounds on a given
698	/// channel, we track which of several buckets those bounds fall into, exponentially decaying
699	/// the probability of each bucket as new samples are added.
700	///
701	/// Default value: 1,250 msat (i.e. willing to pay about 0.125 bps per hop to avoid 78%
702	///                            probability channels, or 0.5bps to avoid a 38% probability
703	///                            channel).
704	///
705	/// [`liquidity_penalty_amount_multiplier_msat`]: Self::liquidity_penalty_amount_multiplier_msat
706	pub historical_liquidity_penalty_amount_multiplier_msat: u64,
707
708	/// Manual penalties used for the given nodes. Allows to set a particular penalty for a given
709	/// node. Note that a manual penalty of `u64::max_value()` means the node would not ever be
710	/// considered during path finding.
711	///
712	/// This is not exported to bindings users
713	pub manual_node_penalties: HashMap<NodeId, u64>,
714
715	/// This penalty is applied when `htlc_maximum_msat` is equal to or larger than half of the
716	/// channel's capacity, (ie. htlc_maximum_msat >= 0.5 * channel_capacity) which makes us
717	/// prefer nodes with a smaller `htlc_maximum_msat`. We treat such nodes preferentially
718	/// as this makes balance discovery attacks harder to execute, thereby creating an incentive
719	/// to restrict `htlc_maximum_msat` and improve privacy.
720	///
721	/// Default value: 250 msat
722	pub anti_probing_penalty_msat: u64,
723
724	/// This penalty is applied when the total amount flowing over a channel exceeds our current
725	/// estimate of the channel's available liquidity. The total amount is the amount of the
726	/// current HTLC plus any HTLCs which we've sent over the same channel.
727	///
728	/// Note that in this case all other penalties, including the
729	/// [`liquidity_penalty_multiplier_msat`] and [`liquidity_penalty_amount_multiplier_msat`]-based
730	/// penalties, as well as the [`base_penalty_msat`] and the [`anti_probing_penalty_msat`], if
731	/// applicable, are still included in the overall penalty.
732	///
733	/// If you wish to avoid creating paths with such channels entirely, setting this to a value of
734	/// `u64::max_value()` will guarantee that.
735	///
736	/// Default value: 1_0000_0000_000 msat (1 Bitcoin)
737	///
738	/// [`liquidity_penalty_multiplier_msat`]: Self::liquidity_penalty_multiplier_msat
739	/// [`liquidity_penalty_amount_multiplier_msat`]: Self::liquidity_penalty_amount_multiplier_msat
740	/// [`base_penalty_msat`]: Self::base_penalty_msat
741	/// [`anti_probing_penalty_msat`]: Self::anti_probing_penalty_msat
742	pub considered_impossible_penalty_msat: u64,
743
744	/// In order to calculate most of the scores above, we must first convert a lower and upper
745	/// bound on the available liquidity in a channel into the probability that we think a payment
746	/// will succeed. That probability is derived from a Probability Density Function for where we
747	/// think the liquidity in a channel likely lies, given such bounds.
748	///
749	/// If this flag is set, that PDF is simply a constant - we assume that the actual available
750	/// liquidity in a channel is just as likely to be at any point between our lower and upper
751	/// bounds.
752	///
753	/// If this flag is *not* set, that PDF is `(x - 0.5*capacity) ^ 2`. That is, we use an
754	/// exponential curve which expects the liquidity of a channel to lie "at the edges". This
755	/// matches experimental results - most routing nodes do not aggressively rebalance their
756	/// channels and flows in the network are often unbalanced, leaving liquidity usually
757	/// unavailable.
758	///
759	/// Thus, for the "best" routes, leave this flag `false`. However, the flag does imply a number
760	/// of floating-point multiplications in the hottest routing code, which may lead to routing
761	/// performance degradation on some machines.
762	///
763	/// Default value: false
764	pub linear_success_probability: bool,
765
766	/// In order to ensure we have knowledge for as many paths as possible, when probing it makes
767	/// sense to bias away from channels for which we have very recent data.
768	///
769	/// This value is a penalty that is applied based on the last time that we updated the bounds
770	/// on the available liquidity in a channel. The specified value is the maximum penalty that
771	/// will be applied.
772	///
773	/// It obviously does not make sense to assign a non-0 value here unless you are using the
774	/// pathfinding result for background probing.
775	///
776	/// Specifically, the following penalty is applied
777	/// `probing_diversity_penalty_msat * max(0, (86400 - current time + last update))^2 / 86400^2` is
778	///
779	/// As this is a maximum value, when setting this you should consider it in relation to the
780	/// other values set to ensure that, at maximum, we strongly avoid paths which we recently
781	/// tried (similar to if they have a low success probability). For example, you might set this
782	/// to be the sum of [`Self::base_penalty_msat`] and
783	/// [`Self::historical_liquidity_penalty_multiplier_msat`] (plus some multiple of their
784	/// corresponding `amount_multiplier`s).
785	///
786	/// Default value: 0
787	pub probing_diversity_penalty_msat: u64,
788}
789
790impl Default for ProbabilisticScoringFeeParameters {
791	fn default() -> Self {
792		Self {
793			base_penalty_msat: 1024,
794			base_penalty_amount_multiplier_msat: 131_072,
795			liquidity_penalty_multiplier_msat: 0,
796			liquidity_penalty_amount_multiplier_msat: 0,
797			manual_node_penalties: new_hash_map(),
798			anti_probing_penalty_msat: 250,
799			considered_impossible_penalty_msat: 1_0000_0000_000,
800			historical_liquidity_penalty_multiplier_msat: 10_000,
801			historical_liquidity_penalty_amount_multiplier_msat: 1_250,
802			linear_success_probability: false,
803			probing_diversity_penalty_msat: 0,
804		}
805	}
806}
807
808impl ProbabilisticScoringFeeParameters {
809	/// Marks the node with the given `node_id` as banned,
810	/// i.e it will be avoided during path finding.
811	pub fn add_banned(&mut self, node_id: &NodeId) {
812		self.manual_node_penalties.insert(*node_id, u64::max_value());
813	}
814
815	/// Marks all nodes in the given list as banned, i.e.,
816	/// they will be avoided during path finding.
817	pub fn add_banned_from_list(&mut self, node_ids: Vec<NodeId>) {
818		for id in node_ids {
819			self.manual_node_penalties.insert(id, u64::max_value());
820		}
821	}
822
823	/// Removes the node with the given `node_id` from the list of nodes to avoid.
824	pub fn remove_banned(&mut self, node_id: &NodeId) {
825		self.manual_node_penalties.remove(node_id);
826	}
827
828	/// Sets a manual penalty for the given node.
829	pub fn set_manual_penalty(&mut self, node_id: &NodeId, penalty: u64) {
830		self.manual_node_penalties.insert(*node_id, penalty);
831	}
832
833	/// Removes the node with the given `node_id` from the list of manual penalties.
834	pub fn remove_manual_penalty(&mut self, node_id: &NodeId) {
835		self.manual_node_penalties.remove(node_id);
836	}
837
838	/// Clears the list of manual penalties that are applied during path finding.
839	pub fn clear_manual_penalties(&mut self) {
840		self.manual_node_penalties = new_hash_map();
841	}
842}
843
844#[cfg(test)]
845impl ProbabilisticScoringFeeParameters {
846	fn zero_penalty() -> Self {
847		Self {
848			base_penalty_msat: 0,
849			base_penalty_amount_multiplier_msat: 0,
850			liquidity_penalty_multiplier_msat: 0,
851			liquidity_penalty_amount_multiplier_msat: 0,
852			historical_liquidity_penalty_multiplier_msat: 0,
853			historical_liquidity_penalty_amount_multiplier_msat: 0,
854			manual_node_penalties: new_hash_map(),
855			anti_probing_penalty_msat: 0,
856			considered_impossible_penalty_msat: 0,
857			linear_success_probability: true,
858			probing_diversity_penalty_msat: 0,
859		}
860	}
861}
862
863/// Parameters for configuring [`ProbabilisticScorer`].
864///
865/// Used to configure decay parameters that are static throughout the lifetime of the scorer.
866/// these decay parameters affect the score of the channel penalty and are not changed on a
867/// per-route penalty cost call.
868#[derive(Copy, Clone, Debug)]
869pub struct ProbabilisticScoringDecayParameters {
870	/// If we aren't learning any new datapoints for a channel, the historical liquidity bounds
871	/// tracking can simply live on with increasingly stale data. Instead, when a channel has not
872	/// seen a liquidity estimate update for this amount of time, the historical datapoints are
873	/// decayed by half.
874	/// For an example of historical_no_updates_half_life being used see [`historical_estimated_channel_liquidity_probabilities`]
875	///
876	/// Note that after 16 or more half lives all historical data will be completely gone.
877	///
878	/// Default value: 14 days
879	///
880	/// [`historical_estimated_channel_liquidity_probabilities`]: ProbabilisticScorer::historical_estimated_channel_liquidity_probabilities
881	pub historical_no_updates_half_life: Duration,
882
883	/// Whenever this amount of time elapses since the last update to a channel's liquidity bounds,
884	/// the distance from the bounds to "zero" is cut in half. In other words, the lower-bound on
885	/// the available liquidity is halved and the upper-bound moves half-way to the channel's total
886	/// capacity.
887	///
888	/// Because halving the liquidity bounds grows the uncertainty on the channel's liquidity,
889	/// the penalty for an amount within the new bounds may change. See the [`ProbabilisticScorer`]
890	/// struct documentation for more info on the way the liquidity bounds are used.
891	///
892	/// For example, if the channel's capacity is 1 million sats, and the current upper and lower
893	/// liquidity bounds are 200,000 sats and 600,000 sats, after this amount of time the upper
894	/// and lower liquidity bounds will be decayed to 100,000 and 800,000 sats.
895	///
896	/// Default value: 30 minutes
897	///
898	/// # Note
899	///
900	/// When not built with the `std` feature, time will never elapse. Therefore, the channel
901	/// liquidity knowledge will never decay except when the bounds cross.
902	pub liquidity_offset_half_life: Duration,
903}
904
905impl Default for ProbabilisticScoringDecayParameters {
906	fn default() -> Self {
907		Self {
908			liquidity_offset_half_life: Duration::from_secs(30 * 60),
909			historical_no_updates_half_life: Duration::from_secs(60 * 60 * 24 * 14),
910		}
911	}
912}
913
914#[cfg(test)]
915impl ProbabilisticScoringDecayParameters {
916	fn zero_penalty() -> Self {
917		Self {
918			liquidity_offset_half_life: Duration::from_secs(30 * 60),
919			historical_no_updates_half_life: Duration::from_secs(60 * 60 * 24 * 14),
920		}
921	}
922}
923
924/// Accounting for channel liquidity balance uncertainty.
925///
926/// Direction is defined in terms of [`NodeId`] partial ordering, where the source node is the
927/// first node in the ordering of the channel's counterparties. Thus, swapping the two liquidity
928/// offset fields gives the opposite direction.
929#[repr(C)] // Force the fields in memory to be in the order we specify
930#[derive(Clone)]
931struct ChannelLiquidity {
932	/// Lower channel liquidity bound in terms of an offset from zero.
933	min_liquidity_offset_msat: u64,
934
935	/// Upper channel liquidity bound in terms of an offset from the effective capacity.
936	max_liquidity_offset_msat: u64,
937
938	liquidity_history: HistoricalLiquidityTracker,
939
940	/// Time when either liquidity bound was last modified as an offset since the unix epoch.
941	last_updated: Duration,
942
943	/// Time when the historical liquidity bounds were last modified as an offset against the unix
944	/// epoch.
945	offset_history_last_updated: Duration,
946
947	/// The last time when the liquidity bounds were updated with new payment information (i.e.
948	/// ignoring decays).
949	last_datapoint_time: Duration,
950}
951
952/// A snapshot of [`ChannelLiquidity`] in one direction assuming a certain channel capacity.
953struct DirectedChannelLiquidity<
954	L: Deref<Target = u64>,
955	HT: Deref<Target = HistoricalLiquidityTracker>,
956	T: Deref<Target = Duration>,
957> {
958	min_liquidity_offset_msat: L,
959	max_liquidity_offset_msat: L,
960	liquidity_history: DirectedHistoricalLiquidityTracker<HT>,
961	capacity_msat: u64,
962	last_updated: T,
963	offset_history_last_updated: T,
964	last_datapoint_time: T,
965}
966
967impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ProbabilisticScorer<G, L>
968where
969	L::Target: Logger,
970{
971	/// Creates a new scorer using the given scoring parameters for sending payments from a node
972	/// through a network graph.
973	pub fn new(
974		decay_params: ProbabilisticScoringDecayParameters, network_graph: G, logger: L,
975	) -> Self {
976		Self {
977			decay_params,
978			network_graph,
979			logger,
980			channel_liquidities: ChannelLiquidities::new(),
981			last_update_time: Duration::from_secs(0),
982		}
983	}
984
985	#[cfg(test)]
986	fn with_channel(mut self, short_channel_id: u64, liquidity: ChannelLiquidity) -> Self {
987		assert!(self.channel_liquidities.insert(short_channel_id, liquidity).is_none());
988		self
989	}
990
991	/// Dump the contents of this scorer into the configured logger.
992	///
993	/// Note that this writes roughly one line per channel for which we have a liquidity estimate,
994	/// which may be a substantial amount of log output.
995	#[rustfmt::skip]
996	pub fn debug_log_liquidity_stats(&self) {
997		let graph = self.network_graph.read_only();
998		for (scid, liq) in self.channel_liquidities.iter() {
999			if let Some(chan_debug) = graph.channels().get(scid) {
1000				let log_direction = |source, target| {
1001					if let Some((directed_info, _)) = chan_debug.as_directed_to(target) {
1002						let amt = directed_info.effective_capacity().as_msat();
1003						let dir_liq = liq.as_directed(source, target, amt);
1004
1005						let min_buckets = &dir_liq.liquidity_history.min_liquidity_offset_history_buckets();
1006						let max_buckets = &dir_liq.liquidity_history.max_liquidity_offset_history_buckets();
1007
1008						log_debug!(self.logger, core::concat!(
1009							"Liquidity from {} to {} via {} is in the range ({}, {}).\n",
1010							"\tHistorical min liquidity bucket relative probabilities:\n",
1011							"\t\t{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}\n",
1012							"\tHistorical max liquidity bucket relative probabilities:\n",
1013							"\t\t{} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}"),
1014							source, target, scid, dir_liq.min_liquidity_msat(), dir_liq.max_liquidity_msat(),
1015							min_buckets[ 0], min_buckets[ 1], min_buckets[ 2], min_buckets[ 3],
1016							min_buckets[ 4], min_buckets[ 5], min_buckets[ 6], min_buckets[ 7],
1017							min_buckets[ 8], min_buckets[ 9], min_buckets[10], min_buckets[11],
1018							min_buckets[12], min_buckets[13], min_buckets[14], min_buckets[15],
1019							min_buckets[16], min_buckets[17], min_buckets[18], min_buckets[19],
1020							min_buckets[20], min_buckets[21], min_buckets[22], min_buckets[23],
1021							min_buckets[24], min_buckets[25], min_buckets[26], min_buckets[27],
1022							min_buckets[28], min_buckets[29], min_buckets[30], min_buckets[31],
1023							// Note that the liquidity buckets are an offset from the edge, so we
1024							// inverse the max order to get the probabilities from zero.
1025							max_buckets[31], max_buckets[30], max_buckets[29], max_buckets[28],
1026							max_buckets[27], max_buckets[26], max_buckets[25], max_buckets[24],
1027							max_buckets[23], max_buckets[22], max_buckets[21], max_buckets[20],
1028							max_buckets[19], max_buckets[18], max_buckets[17], max_buckets[16],
1029							max_buckets[15], max_buckets[14], max_buckets[13], max_buckets[12],
1030							max_buckets[11], max_buckets[10], max_buckets[ 9], max_buckets[ 8],
1031							max_buckets[ 7], max_buckets[ 6], max_buckets[ 5], max_buckets[ 4],
1032							max_buckets[ 3], max_buckets[ 2], max_buckets[ 1], max_buckets[ 0]);
1033					} else {
1034						log_debug!(self.logger, "No amount known for SCID {} from {:?} to {:?}", scid, source, target);
1035					}
1036				};
1037
1038				log_direction(&chan_debug.node_one, &chan_debug.node_two);
1039				log_direction(&chan_debug.node_two, &chan_debug.node_one);
1040			} else {
1041				log_debug!(self.logger, "No network graph entry for SCID {}", scid);
1042			}
1043		}
1044	}
1045
1046	/// Query the estimated minimum and maximum liquidity available for sending a payment over the
1047	/// channel with `scid` towards the given `target` node.
1048	pub fn estimated_channel_liquidity_range(
1049		&self, scid: u64, target: &NodeId,
1050	) -> Option<(u64, u64)> {
1051		let graph = self.network_graph.read_only();
1052
1053		if let Some(chan) = graph.channels().get(&scid) {
1054			if let Some(liq) = self.channel_liquidities.get(&scid) {
1055				if let Some((directed_info, source)) = chan.as_directed_to(target) {
1056					let amt = directed_info.effective_capacity().as_msat();
1057					let dir_liq = liq.as_directed(source, target, amt);
1058					return Some((dir_liq.min_liquidity_msat(), dir_liq.max_liquidity_msat()));
1059				}
1060			}
1061		}
1062		None
1063	}
1064
1065	/// Query the historical estimated minimum and maximum liquidity available for sending a
1066	/// payment over the channel with `scid` towards the given `target` node.
1067	///
1068	/// Returns two sets of 32 buckets. The first set describes the lower-bound liquidity history,
1069	/// the second set describes the upper-bound liquidity history. Each bucket describes the
1070	/// relative frequency at which we've seen a liquidity bound in the bucket's range relative to
1071	/// the channel's total capacity, on an arbitrary scale. Because the values are slowly decayed,
1072	/// more recent data points are weighted more heavily than older datapoints.
1073	///
1074	/// Note that the range of each bucket varies by its location to provide more granular results
1075	/// at the edges of a channel's capacity, where it is more likely to sit.
1076	///
1077	/// When scoring, the estimated probability that an upper-/lower-bound lies in a given bucket
1078	/// is calculated by dividing that bucket's value with the total value of all buckets.
1079	///
1080	/// For example, using a lower bucket count for illustrative purposes, a value of
1081	/// `[0, 0, 0, ..., 0, 32]` indicates that we believe the probability of a bound being very
1082	/// close to the channel's capacity to be 100%, and have never (recently) seen it in any other
1083	/// bucket. A value of `[31, 0, 0, ..., 0, 0, 32]` indicates we've seen the bound being both
1084	/// in the top and bottom bucket, and roughly with similar (recent) frequency.
1085	///
1086	/// Because the datapoints are decayed slowly over time, values will eventually return to
1087	/// `Some(([0; 32], [0; 32]))` or `None` if no data remains for a channel.
1088	///
1089	/// In order to fetch a single success probability from the buckets provided here, as used in
1090	/// the scoring model, see [`Self::historical_estimated_payment_success_probability`].
1091	#[rustfmt::skip]
1092	pub fn historical_estimated_channel_liquidity_probabilities(&self, scid: u64, target: &NodeId)
1093	-> Option<([u16; 32], [u16; 32])> {
1094		let graph = self.network_graph.read_only();
1095
1096		if let Some(chan) = graph.channels().get(&scid) {
1097			if let Some(liq) = self.channel_liquidities.get(&scid) {
1098				if let Some((directed_info, source)) = chan.as_directed_to(target) {
1099					let amt = directed_info.effective_capacity().as_msat();
1100					let dir_liq = liq.as_directed(source, target, amt);
1101
1102					let min_buckets = *dir_liq.liquidity_history.min_liquidity_offset_history_buckets();
1103					let mut max_buckets = *dir_liq.liquidity_history.max_liquidity_offset_history_buckets();
1104
1105					// Note that the liquidity buckets are an offset from the edge, so we inverse
1106					// the max order to get the probabilities from zero.
1107					max_buckets.reverse();
1108					return Some((min_buckets, max_buckets));
1109				}
1110			}
1111		}
1112		None
1113	}
1114
1115	/// Query the probability of payment success sending the given `amount_msat` over the channel
1116	/// with `scid` towards the given `target` node, based on the historical estimated liquidity
1117	/// bounds.
1118	///
1119	/// Returns `None` if:
1120	///  - the given channel is not in the network graph, the provided `target` is not a party to
1121	///    the channel, or we don't have forwarding parameters for either direction in the channel.
1122	///  - `allow_fallback_estimation` is *not* set and there is no (or insufficient) historical
1123	///    data for the given channel.
1124	///
1125	/// These are the same bounds as returned by
1126	/// [`Self::historical_estimated_channel_liquidity_probabilities`] (but not those returned by
1127	/// [`Self::estimated_channel_liquidity_range`]).
1128	#[rustfmt::skip]
1129	pub fn historical_estimated_payment_success_probability(
1130		&self, scid: u64, target: &NodeId, amount_msat: u64, params: &ProbabilisticScoringFeeParameters,
1131		allow_fallback_estimation: bool,
1132	) -> Option<f64> {
1133		let graph = self.network_graph.read_only();
1134
1135		if let Some(chan) = graph.channels().get(&scid) {
1136			if let Some((directed_info, source)) = chan.as_directed_to(target) {
1137				if let Some(liq) = self.channel_liquidities.get(&scid) {
1138					let capacity_msat = directed_info.effective_capacity().as_msat();
1139					let dir_liq = liq.as_directed(source, target, capacity_msat);
1140
1141					let res = dir_liq.liquidity_history.calculate_success_probability_times_billion(
1142						&params, amount_msat, capacity_msat
1143					).map(|p| p as f64 / (1024 * 1024 * 1024) as f64);
1144					if res.is_some() {
1145						return res;
1146					}
1147				}
1148				if allow_fallback_estimation {
1149					let amt = amount_msat;
1150					return Some(
1151						self.calc_live_prob(scid, source, target, directed_info, amt, params, true)
1152					);
1153				}
1154			}
1155		}
1156		None
1157	}
1158
1159	#[rustfmt::skip]
1160	fn calc_live_prob(
1161		&self, scid: u64, source: &NodeId, target: &NodeId, directed_info: DirectedChannelInfo,
1162		amt: u64, params: &ProbabilisticScoringFeeParameters,
1163		min_zero_penalty: bool,
1164	) -> f64 {
1165		let capacity_msat = directed_info.effective_capacity().as_msat();
1166		let dummy_liq = ChannelLiquidity::new(Duration::ZERO);
1167		let liq = self.channel_liquidities.get(&scid)
1168			.unwrap_or(&dummy_liq)
1169			.as_directed(&source, &target, capacity_msat);
1170		let min_liq = liq.min_liquidity_msat();
1171		let max_liq = liq.max_liquidity_msat();
1172		if amt <= liq.min_liquidity_msat() {
1173			return 1.0;
1174		} else if amt > liq.max_liquidity_msat() {
1175			return 0.0;
1176		}
1177		let (num, den) =
1178			success_probability(amt, min_liq, max_liq, capacity_msat, &params, min_zero_penalty);
1179		num as f64 / den as f64
1180	}
1181
1182	/// Query the probability of payment success sending the given `amount_msat` over the channel
1183	/// with `scid` towards the given `target` node, based on the live estimated liquidity bounds.
1184	///
1185	/// This will return `Some` for any channel which is present in the [`NetworkGraph`], including
1186	/// if we have no bound information beside the channel's capacity.
1187	#[rustfmt::skip]
1188	pub fn live_estimated_payment_success_probability(
1189		&self, scid: u64, target: &NodeId, amount_msat: u64, params: &ProbabilisticScoringFeeParameters,
1190	) -> Option<f64> {
1191		let graph = self.network_graph.read_only();
1192
1193		if let Some(chan) = graph.channels().get(&scid) {
1194			if let Some((directed_info, source)) = chan.as_directed_to(target) {
1195				return Some(self.calc_live_prob(scid, source, target, directed_info, amount_msat, params, false));
1196			}
1197		}
1198		None
1199	}
1200
1201	/// Overwrite the scorer state with the given external scores.
1202	pub fn set_scores(&mut self, external_scores: ChannelLiquidities) {
1203		_ = mem::replace(&mut self.channel_liquidities, external_scores);
1204	}
1205
1206	/// Returns the current scores.
1207	pub fn scores(&self) -> &ChannelLiquidities {
1208		&self.channel_liquidities
1209	}
1210}
1211
1212impl ChannelLiquidity {
1213	fn new(last_updated: Duration) -> Self {
1214		Self {
1215			min_liquidity_offset_msat: 0,
1216			max_liquidity_offset_msat: 0,
1217			liquidity_history: HistoricalLiquidityTracker::new(),
1218			last_updated,
1219			offset_history_last_updated: last_updated,
1220			last_datapoint_time: last_updated,
1221		}
1222	}
1223
1224	#[rustfmt::skip]
1225	fn merge(&mut self, other: &Self) {
1226		// Take average for min/max liquidity offsets.
1227		self.min_liquidity_offset_msat = (self.min_liquidity_offset_msat + other.min_liquidity_offset_msat) / 2;
1228		self.max_liquidity_offset_msat = (self.max_liquidity_offset_msat + other.max_liquidity_offset_msat) / 2;
1229
1230		// Merge historical liquidity data.
1231		self.liquidity_history.merge(&other.liquidity_history);
1232	}
1233
1234	/// Returns a view of the channel liquidity directed from `source` to `target` assuming
1235	/// `capacity_msat`.
1236	#[rustfmt::skip]
1237	fn as_directed(
1238		&self, source: &NodeId, target: &NodeId, capacity_msat: u64,
1239	) -> DirectedChannelLiquidity<&u64, &HistoricalLiquidityTracker, &Duration> {
1240		let source_less_than_target = source < target;
1241		let (min_liquidity_offset_msat, max_liquidity_offset_msat) =
1242			if source_less_than_target {
1243				(&self.min_liquidity_offset_msat, &self.max_liquidity_offset_msat)
1244			} else {
1245				(&self.max_liquidity_offset_msat, &self.min_liquidity_offset_msat)
1246			};
1247
1248		DirectedChannelLiquidity {
1249			min_liquidity_offset_msat,
1250			max_liquidity_offset_msat,
1251			liquidity_history: self.liquidity_history.as_directed(source_less_than_target),
1252			capacity_msat,
1253			last_updated: &self.last_updated,
1254			offset_history_last_updated: &self.offset_history_last_updated,
1255			last_datapoint_time: &self.last_datapoint_time,
1256		}
1257	}
1258
1259	/// Returns a mutable view of the channel liquidity directed from `source` to `target` assuming
1260	/// `capacity_msat`.
1261	#[rustfmt::skip]
1262	fn as_directed_mut(
1263		&mut self, source: &NodeId, target: &NodeId, capacity_msat: u64,
1264	) -> DirectedChannelLiquidity<&mut u64, &mut HistoricalLiquidityTracker, &mut Duration> {
1265		let source_less_than_target = source < target;
1266		let (min_liquidity_offset_msat, max_liquidity_offset_msat) =
1267			if source_less_than_target {
1268				(&mut self.min_liquidity_offset_msat, &mut self.max_liquidity_offset_msat)
1269			} else {
1270				(&mut self.max_liquidity_offset_msat, &mut self.min_liquidity_offset_msat)
1271			};
1272
1273		DirectedChannelLiquidity {
1274			min_liquidity_offset_msat,
1275			max_liquidity_offset_msat,
1276			liquidity_history: self.liquidity_history.as_directed_mut(source_less_than_target),
1277			capacity_msat,
1278			last_updated: &mut self.last_updated,
1279			offset_history_last_updated: &mut self.offset_history_last_updated,
1280			last_datapoint_time: &mut self.last_datapoint_time,
1281		}
1282	}
1283
1284	fn decayed_offset(
1285		&self, offset: u64, duration_since_epoch: Duration,
1286		decay_params: ProbabilisticScoringDecayParameters,
1287	) -> u64 {
1288		let half_life = decay_params.liquidity_offset_half_life.as_secs_f64();
1289		if half_life != 0.0 {
1290			let elapsed_time = duration_since_epoch.saturating_sub(self.last_updated).as_secs_f64();
1291			((offset as f64) * powf64(0.5, elapsed_time / half_life)) as u64
1292		} else {
1293			0
1294		}
1295	}
1296}
1297
1298/// Bounds `-log10` to avoid excessive liquidity penalties for payments with low success
1299/// probabilities.
1300const NEGATIVE_LOG10_UPPER_BOUND: u64 = 2;
1301
1302/// The rough cutoff at which our precision falls off and we should stop bothering to try to log a
1303/// ratio, as X in 1/X.
1304const PRECISION_LOWER_BOUND_DENOMINATOR: u64 = log_approx::LOWER_BITS_BOUND;
1305
1306/// The divisor used when computing the amount penalty.
1307const AMOUNT_PENALTY_DIVISOR: u64 = 1 << 20;
1308const BASE_AMOUNT_PENALTY_DIVISOR: u64 = 1 << 30;
1309
1310/// Raises three `f64`s to the 9th power, without `powi` because it requires `std` (dunno why).
1311#[inline(always)]
1312fn three_f64_pow_9(a: f64, b: f64, c: f64) -> (f64, f64, f64) {
1313	let (a2, b2, c2) = (a * a, b * b, c * c);
1314	let (a4, b4, c4) = (a2 * a2, b2 * b2, c2 * c2);
1315	(a * a4 * a4, b * b4 * b4, c * c4 * c4)
1316}
1317
1318/// If we have no knowledge of the channel, we scale probability down by a multiple of ~82% for the
1319/// historical model by multiplying the denominator of a success probability by this before
1320/// dividing by 64.
1321///
1322/// This number (as well as the PDF) was picked experimentally on probing results to maximize the
1323/// log-loss of succeeding and failing hops.
1324///
1325/// Note that we prefer to increase the denominator rather than decrease the numerator as the
1326/// denominator is more likely to be larger and thus provide greater precision. This is mostly an
1327/// overoptimization but makes a large difference in tests.
1328const MIN_ZERO_IMPLIES_NO_SUCCESSES_PENALTY_ON_64: u64 = 78;
1329
1330#[inline(always)]
1331#[rustfmt::skip]
1332fn linear_success_probability(
1333	total_inflight_amount_msat: u64, min_liquidity_msat: u64, max_liquidity_msat: u64,
1334	min_zero_implies_no_successes: bool,
1335) -> (u64, u64) {
1336	let (numerator, mut denominator) =
1337		(max_liquidity_msat - total_inflight_amount_msat,
1338		(max_liquidity_msat - min_liquidity_msat).saturating_add(1));
1339
1340	if min_zero_implies_no_successes && min_liquidity_msat == 0 &&
1341		denominator < u64::max_value() / MIN_ZERO_IMPLIES_NO_SUCCESSES_PENALTY_ON_64
1342	{
1343		denominator = denominator * MIN_ZERO_IMPLIES_NO_SUCCESSES_PENALTY_ON_64 / 64
1344	}
1345
1346	(numerator, denominator)
1347}
1348
1349/// Returns a (numerator, denominator) pair each between 0 and 0.0078125, inclusive.
1350#[inline(always)]
1351#[rustfmt::skip]
1352fn nonlinear_success_probability(
1353	total_inflight_amount_msat: u64, min_liquidity_msat: u64, max_liquidity_msat: u64,
1354	capacity_msat: u64, min_zero_implies_no_successes: bool,
1355) -> (f64, f64) {
1356	let capacity = capacity_msat as f64;
1357	let max = (max_liquidity_msat as f64) / capacity;
1358	let min = (min_liquidity_msat as f64) / capacity;
1359	let amount = (total_inflight_amount_msat as f64) / capacity;
1360
1361	// Assume the channel has a probability density function of
1362	// `128 * (1/256 + 9*(x - 0.5)^8)` for values from 0 to 1 (where 1 is the channel's
1363	// full capacity). The success probability given some liquidity bounds is thus the
1364	// integral under the curve from the amount to maximum estimated liquidity, divided by
1365	// the same integral from the minimum to the maximum estimated liquidity bounds.
1366	//
1367	// Because the integral from x to y is simply
1368	// `128*(1/256 * (y - 0.5) + (y - 0.5)^9) - 128*(1/256 * (x - 0.5) + (x - 0.5)^9), we
1369	// can calculate the cumulative density function between the min/max bounds trivially.
1370	// Note that we don't bother to normalize the CDF to total to 1 (using the 128
1371	// multiple), as it will come out in the division of num / den.
1372	let (max_norm, min_norm, amt_norm) = (max - 0.5, min - 0.5, amount - 0.5);
1373	let (max_pow, min_pow, amt_pow) = three_f64_pow_9(max_norm, min_norm, amt_norm);
1374	let (max_v, min_v, amt_v) = (max_pow + max_norm / 256.0, min_pow + min_norm / 256.0, amt_pow + amt_norm / 256.0);
1375	let mut denominator = max_v - min_v;
1376	let numerator = max_v - amt_v;
1377
1378	if min_zero_implies_no_successes && min_liquidity_msat == 0 {
1379		denominator = denominator * (MIN_ZERO_IMPLIES_NO_SUCCESSES_PENALTY_ON_64 as f64) / 64.0;
1380	}
1381
1382	(numerator, denominator)
1383}
1384
1385/// Given liquidity bounds, calculates the success probability (in the form of a numerator and
1386/// denominator) of an HTLC. This is a key assumption in our scoring models.
1387///
1388/// `total_inflight_amount_msat` includes the amount of the HTLC and any HTLCs in flight over the
1389/// channel.
1390///
1391/// min_zero_implies_no_successes signals that a `min_liquidity_msat` of 0 means we've not
1392/// (recently) seen an HTLC successfully complete over this channel.
1393#[inline(always)]
1394#[rustfmt::skip]
1395fn success_probability_float(
1396	total_inflight_amount_msat: u64, min_liquidity_msat: u64, max_liquidity_msat: u64,
1397	capacity_msat: u64, params: &ProbabilisticScoringFeeParameters,
1398	min_zero_implies_no_successes: bool,
1399) -> (f64, f64) {
1400	debug_assert!(min_liquidity_msat <= total_inflight_amount_msat);
1401	debug_assert!(total_inflight_amount_msat < max_liquidity_msat);
1402	debug_assert!(max_liquidity_msat <= capacity_msat);
1403
1404	if params.linear_success_probability {
1405		let (numerator, denominator) = linear_success_probability(total_inflight_amount_msat, min_liquidity_msat, max_liquidity_msat, min_zero_implies_no_successes);
1406		(numerator as f64, denominator as f64)
1407	} else {
1408		nonlinear_success_probability(total_inflight_amount_msat, min_liquidity_msat, max_liquidity_msat, capacity_msat, min_zero_implies_no_successes)
1409	}
1410}
1411
1412#[inline(always)]
1413/// Identical to [`success_probability_float`] but returns integer numerator and denominators.
1414///
1415/// Must not return a numerator or denominator greater than 2^31 for arguments less than 2^31.
1416#[rustfmt::skip]
1417fn success_probability(
1418	total_inflight_amount_msat: u64, min_liquidity_msat: u64, max_liquidity_msat: u64,
1419	capacity_msat: u64, params: &ProbabilisticScoringFeeParameters,
1420	min_zero_implies_no_successes: bool,
1421) -> (u64, u64) {
1422	debug_assert!(min_liquidity_msat <= total_inflight_amount_msat);
1423	debug_assert!(total_inflight_amount_msat < max_liquidity_msat);
1424	debug_assert!(max_liquidity_msat <= capacity_msat);
1425
1426	if params.linear_success_probability {
1427		linear_success_probability(total_inflight_amount_msat, min_liquidity_msat, max_liquidity_msat, min_zero_implies_no_successes)
1428	} else {
1429		// We calculate the nonlinear probabilities using floats anyway, so just stub out to
1430		// the float version and then convert to integers.
1431		let (num, den) = nonlinear_success_probability(
1432			total_inflight_amount_msat, min_liquidity_msat, max_liquidity_msat, capacity_msat,
1433			min_zero_implies_no_successes,
1434		);
1435
1436		// Because our numerator and denominator max out at 0.0078125 we need to multiply them
1437		// by quite a large factor to get something useful (ideally in the 2^30 range).
1438		const BILLIONISH: f64 = 1024.0 * 1024.0 * 1024.0 * 64.0;
1439		let numerator = (num * BILLIONISH) as u64 + 1;
1440		let denominator = (den * BILLIONISH) as u64 + 1;
1441		debug_assert!(numerator <= 1 << 30, "Got large numerator ({}) from float {}.", numerator, num);
1442		debug_assert!(denominator <= 1 << 30, "Got large denominator ({}) from float {}.", denominator, den);
1443		(numerator, denominator)
1444	}
1445}
1446
1447impl<
1448		L: Deref<Target = u64>,
1449		HT: Deref<Target = HistoricalLiquidityTracker>,
1450		T: Deref<Target = Duration>,
1451	> DirectedChannelLiquidity<L, HT, T>
1452{
1453	/// Returns a liquidity penalty for routing the given HTLC `amount_msat` through the channel in
1454	/// this direction.
1455	#[rustfmt::skip]
1456	fn penalty_msat(
1457		&self, amount_msat: u64, inflight_htlc_msat: u64, last_update_time: Duration,
1458		score_params: &ProbabilisticScoringFeeParameters,
1459	) -> u64 {
1460		let total_inflight_amount_msat = amount_msat.saturating_add(inflight_htlc_msat);
1461		let available_capacity = self.capacity_msat;
1462		let max_liquidity_msat = self.max_liquidity_msat();
1463		let min_liquidity_msat = core::cmp::min(self.min_liquidity_msat(), max_liquidity_msat);
1464
1465		let mut res = 0;
1466		if score_params.liquidity_penalty_multiplier_msat != 0 ||
1467		   score_params.liquidity_penalty_amount_multiplier_msat != 0 {
1468			if total_inflight_amount_msat <= min_liquidity_msat {
1469				// If the in-flight is less than the minimum liquidity estimate, we don't assign a
1470				// liquidity penalty at all (as the success probability is 100%).
1471			} else if total_inflight_amount_msat >= max_liquidity_msat {
1472				// Equivalent to hitting the else clause below with the amount equal to the effective
1473				// capacity and without any certainty on the liquidity upper bound, plus the
1474				// impossibility penalty.
1475				let negative_log10_times_2048 = NEGATIVE_LOG10_UPPER_BOUND * 2048;
1476				res = Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
1477						score_params.liquidity_penalty_multiplier_msat,
1478						score_params.liquidity_penalty_amount_multiplier_msat);
1479			} else {
1480				let (numerator, denominator) = success_probability(
1481					total_inflight_amount_msat, min_liquidity_msat, max_liquidity_msat,
1482					available_capacity, score_params, false,
1483				);
1484				if denominator - numerator < denominator / PRECISION_LOWER_BOUND_DENOMINATOR {
1485					// If the failure probability is < 1.5625% (as 1 - numerator/denominator < 1/64),
1486					// don't bother trying to use the log approximation as it gets too noisy to be
1487					// particularly helpful, instead just round down to 0.
1488				} else {
1489					let negative_log10_times_2048 =
1490						log_approx::negative_log10_times_2048(numerator, denominator);
1491					res = Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
1492						score_params.liquidity_penalty_multiplier_msat,
1493						score_params.liquidity_penalty_amount_multiplier_msat);
1494				}
1495			}
1496		}
1497
1498		if total_inflight_amount_msat >= max_liquidity_msat {
1499			res = res.saturating_add(score_params.considered_impossible_penalty_msat);
1500		}
1501
1502		if total_inflight_amount_msat >= available_capacity {
1503			// We're trying to send more than the capacity, use a max penalty.
1504			res = res.saturating_add(Self::combined_penalty_msat(amount_msat,
1505				NEGATIVE_LOG10_UPPER_BOUND * 2048,
1506				score_params.historical_liquidity_penalty_multiplier_msat,
1507				score_params.historical_liquidity_penalty_amount_multiplier_msat));
1508			return res;
1509		}
1510
1511		if score_params.historical_liquidity_penalty_multiplier_msat != 0 ||
1512		   score_params.historical_liquidity_penalty_amount_multiplier_msat != 0 {
1513			if let Some(cumulative_success_prob_times_billion) = self.liquidity_history
1514				.calculate_success_probability_times_billion(
1515					score_params, total_inflight_amount_msat, self.capacity_msat
1516				)
1517			{
1518				let historical_negative_log10_times_2048 =
1519					log_approx::negative_log10_times_2048(cumulative_success_prob_times_billion + 1, 1024 * 1024 * 1024);
1520				res = res.saturating_add(Self::combined_penalty_msat(amount_msat,
1521					historical_negative_log10_times_2048, score_params.historical_liquidity_penalty_multiplier_msat,
1522					score_params.historical_liquidity_penalty_amount_multiplier_msat));
1523			} else {
1524				// If we don't have any valid points (or, once decayed, we have less than a full
1525				// point), redo the non-historical calculation with no liquidity bounds tracked and
1526				// the historical penalty multipliers.
1527				let (numerator, denominator) = success_probability(
1528					total_inflight_amount_msat, 0, available_capacity, available_capacity,
1529					score_params, true,
1530				);
1531				let negative_log10_times_2048 =
1532					log_approx::negative_log10_times_2048(numerator, denominator);
1533				res = res.saturating_add(Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
1534					score_params.historical_liquidity_penalty_multiplier_msat,
1535					score_params.historical_liquidity_penalty_amount_multiplier_msat));
1536			}
1537		}
1538
1539		if score_params.probing_diversity_penalty_msat != 0 {
1540			// We use `last_update_time` as a stand-in for the current time as we don't want to
1541			// fetch the current time in every score call (slowing things down substantially on
1542			// some platforms where a syscall is required), don't want to add an unnecessary `std`
1543			// requirement. Assuming we're probing somewhat regularly, it should reliably be close
1544			// to the current time, (and using the last the last time we probed is also fine here).
1545			let time_since_update = last_update_time.saturating_sub(*self.last_datapoint_time);
1546			let mul = Duration::from_secs(60 * 60 * 24).saturating_sub(time_since_update).as_secs();
1547			let penalty = score_params.probing_diversity_penalty_msat.saturating_mul(mul * mul);
1548			res = res.saturating_add(penalty / ((60 * 60 * 24) * (60 * 60 * 24)));
1549		}
1550
1551		res
1552	}
1553
1554	/// Computes the liquidity penalty from the penalty multipliers.
1555	#[inline(always)]
1556	#[rustfmt::skip]
1557	fn combined_penalty_msat(amount_msat: u64, mut negative_log10_times_2048: u64,
1558		liquidity_penalty_multiplier_msat: u64, liquidity_penalty_amount_multiplier_msat: u64,
1559	) -> u64 {
1560		negative_log10_times_2048 =
1561			negative_log10_times_2048.min(NEGATIVE_LOG10_UPPER_BOUND * 2048);
1562
1563		// Upper bound the liquidity penalty to ensure some channel is selected.
1564		let liquidity_penalty_msat = negative_log10_times_2048
1565			.saturating_mul(liquidity_penalty_multiplier_msat) / 2048;
1566		let amount_penalty_msat = negative_log10_times_2048
1567			.saturating_mul(liquidity_penalty_amount_multiplier_msat)
1568			.saturating_mul(amount_msat) / 2048 / AMOUNT_PENALTY_DIVISOR;
1569
1570		liquidity_penalty_msat.saturating_add(amount_penalty_msat)
1571	}
1572
1573	/// Returns the lower bound of the channel liquidity balance in this direction.
1574	#[inline(always)]
1575	fn min_liquidity_msat(&self) -> u64 {
1576		*self.min_liquidity_offset_msat
1577	}
1578
1579	/// Returns the upper bound of the channel liquidity balance in this direction.
1580	#[inline(always)]
1581	#[rustfmt::skip]
1582	fn max_liquidity_msat(&self) -> u64 {
1583		self.capacity_msat
1584			.saturating_sub(*self.max_liquidity_offset_msat)
1585	}
1586}
1587
1588impl<
1589		L: DerefMut<Target = u64>,
1590		HT: DerefMut<Target = HistoricalLiquidityTracker>,
1591		T: DerefMut<Target = Duration>,
1592	> DirectedChannelLiquidity<L, HT, T>
1593{
1594	/// Adjusts the channel liquidity balance bounds when failing to route `amount_msat`.
1595	#[rustfmt::skip]
1596	fn failed_at_channel<Log: Deref>(
1597		&mut self, amount_msat: u64, duration_since_epoch: Duration, chan_descr: fmt::Arguments, logger: &Log
1598	) where Log::Target: Logger {
1599		let existing_max_msat = self.max_liquidity_msat();
1600		if amount_msat < existing_max_msat {
1601			log_debug!(logger, "Setting max liquidity of {} from {} to {}", chan_descr, existing_max_msat, amount_msat);
1602			self.set_max_liquidity_msat(amount_msat, duration_since_epoch);
1603		} else {
1604			log_trace!(logger, "Max liquidity of {} is {} (already less than or equal to {})",
1605				chan_descr, existing_max_msat, amount_msat);
1606		}
1607		self.update_history_buckets(0, duration_since_epoch);
1608		*self.last_datapoint_time = duration_since_epoch;
1609	}
1610
1611	/// Adjusts the channel liquidity balance bounds when failing to route `amount_msat` downstream.
1612	#[rustfmt::skip]
1613	fn failed_downstream<Log: Deref>(
1614		&mut self, amount_msat: u64, duration_since_epoch: Duration, chan_descr: fmt::Arguments, logger: &Log
1615	) where Log::Target: Logger {
1616		let existing_min_msat = self.min_liquidity_msat();
1617		if amount_msat > existing_min_msat {
1618			log_debug!(logger, "Setting min liquidity of {} from {} to {}", existing_min_msat, chan_descr, amount_msat);
1619			self.set_min_liquidity_msat(amount_msat, duration_since_epoch);
1620		} else {
1621			log_trace!(logger, "Min liquidity of {} is {} (already greater than or equal to {})",
1622				chan_descr, existing_min_msat, amount_msat);
1623		}
1624		self.update_history_buckets(0, duration_since_epoch);
1625		*self.last_datapoint_time = duration_since_epoch;
1626	}
1627
1628	/// Adjusts the channel liquidity balance bounds when successfully routing `amount_msat`.
1629	#[rustfmt::skip]
1630	fn successful<Log: Deref>(&mut self,
1631		amount_msat: u64, duration_since_epoch: Duration, chan_descr: fmt::Arguments, logger: &Log
1632	) where Log::Target: Logger {
1633		let max_liquidity_msat = self.max_liquidity_msat().checked_sub(amount_msat).unwrap_or(0);
1634		log_debug!(logger, "Subtracting {} from max liquidity of {} (setting it to {})", amount_msat, chan_descr, max_liquidity_msat);
1635		self.set_max_liquidity_msat(max_liquidity_msat, duration_since_epoch);
1636		*self.last_datapoint_time = duration_since_epoch;
1637		self.update_history_buckets(amount_msat, duration_since_epoch);
1638	}
1639
1640	/// Updates the history buckets for this channel. Because the history buckets track what we now
1641	/// know about the channel's state *prior to our payment* (i.e. what we assume is "steady
1642	/// state"), we allow the caller to set an offset applied to our liquidity bounds which
1643	/// represents the amount of the successful payment we just made.
1644	fn update_history_buckets(&mut self, bucket_offset_msat: u64, duration_since_epoch: Duration) {
1645		self.liquidity_history.track_datapoint(
1646			*self.min_liquidity_offset_msat + bucket_offset_msat,
1647			self.max_liquidity_offset_msat.saturating_sub(bucket_offset_msat),
1648			self.capacity_msat,
1649		);
1650		*self.offset_history_last_updated = duration_since_epoch;
1651	}
1652
1653	/// Adjusts the lower bound of the channel liquidity balance in this direction.
1654	fn set_min_liquidity_msat(&mut self, amount_msat: u64, duration_since_epoch: Duration) {
1655		*self.min_liquidity_offset_msat = amount_msat;
1656		if amount_msat > self.max_liquidity_msat() {
1657			*self.max_liquidity_offset_msat = 0;
1658		}
1659		*self.last_updated = duration_since_epoch;
1660	}
1661
1662	/// Adjusts the upper bound of the channel liquidity balance in this direction.
1663	fn set_max_liquidity_msat(&mut self, amount_msat: u64, duration_since_epoch: Duration) {
1664		*self.max_liquidity_offset_msat = self.capacity_msat.checked_sub(amount_msat).unwrap_or(0);
1665		if amount_msat < *self.min_liquidity_offset_msat {
1666			*self.min_liquidity_offset_msat = 0;
1667		}
1668		*self.last_updated = duration_since_epoch;
1669	}
1670}
1671
1672impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreLookUp for ProbabilisticScorer<G, L>
1673where
1674	L::Target: Logger,
1675{
1676	type ScoreParams = ProbabilisticScoringFeeParameters;
1677	#[rustfmt::skip]
1678	fn channel_penalty_msat(
1679		&self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &ProbabilisticScoringFeeParameters
1680	) -> u64 {
1681		let (scid, target) = match candidate {
1682			CandidateRouteHop::PublicHop(PublicHopCandidate { info, short_channel_id }) => {
1683				(short_channel_id, info.target())
1684			},
1685			_ => return 0,
1686		};
1687		let source = candidate.source();
1688		if let Some(penalty) = score_params.manual_node_penalties.get(target) {
1689			return *penalty;
1690		}
1691
1692		let base_penalty_msat = score_params.base_penalty_msat.saturating_add(
1693			score_params.base_penalty_amount_multiplier_msat
1694				.saturating_mul(usage.amount_msat) / BASE_AMOUNT_PENALTY_DIVISOR);
1695
1696		let mut anti_probing_penalty_msat = 0;
1697		match usage.effective_capacity {
1698			EffectiveCapacity::ExactLiquidity { liquidity_msat: amount_msat } |
1699				EffectiveCapacity::HintMaxHTLC { amount_msat } =>
1700			{
1701				if usage.amount_msat > amount_msat {
1702					return u64::max_value();
1703				} else {
1704					return base_penalty_msat;
1705				}
1706			},
1707			EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat } => {
1708				if htlc_maximum_msat >= capacity_msat/2 {
1709					anti_probing_penalty_msat = score_params.anti_probing_penalty_msat;
1710				}
1711			},
1712			_ => {},
1713		}
1714
1715		let capacity_msat = usage.effective_capacity.as_msat();
1716		let time = self.last_update_time;
1717		self.channel_liquidities
1718			.get(scid)
1719			.unwrap_or(&ChannelLiquidity::new(Duration::ZERO))
1720			.as_directed(&source, &target, capacity_msat)
1721			.penalty_msat(usage.amount_msat, usage.inflight_htlc_msat, time, score_params)
1722			.saturating_add(anti_probing_penalty_msat)
1723			.saturating_add(base_penalty_msat)
1724	}
1725}
1726
1727impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for ProbabilisticScorer<G, L>
1728where
1729	L::Target: Logger,
1730{
1731	#[rustfmt::skip]
1732	fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
1733		let amount_msat = path.final_value_msat();
1734		log_trace!(self.logger, "Scoring path through to SCID {} as having failed at {} msat", short_channel_id, amount_msat);
1735		let network_graph = self.network_graph.read_only();
1736		for (hop_idx, hop) in path.hops.iter().enumerate() {
1737			let target = NodeId::from_pubkey(&hop.pubkey);
1738			let channel_directed_from_source = network_graph.channels()
1739				.get(&hop.short_channel_id)
1740				.and_then(|channel| channel.as_directed_to(&target));
1741
1742			let at_failed_channel = hop.short_channel_id == short_channel_id;
1743			if at_failed_channel && hop_idx == 0 {
1744				log_warn!(self.logger, "Payment failed at the first hop - we do not attempt to learn channel info in such cases as we can directly observe local state.\n\tBecause we know the local state, we should generally not see failures here - this may be an indication that your channel peer on channel {} is broken and you may wish to close the channel.", hop.short_channel_id);
1745			}
1746
1747			// Only score announced channels.
1748			if let Some((channel, source)) = channel_directed_from_source {
1749				let capacity_msat = channel.effective_capacity().as_msat();
1750				if at_failed_channel {
1751					self.channel_liquidities
1752						.entry(hop.short_channel_id)
1753						.or_insert_with(|| ChannelLiquidity::new(duration_since_epoch))
1754						.as_directed_mut(source, &target, capacity_msat)
1755						.failed_at_channel(amount_msat, duration_since_epoch,
1756							format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
1757				} else {
1758					self.channel_liquidities
1759						.entry(hop.short_channel_id)
1760						.or_insert_with(|| ChannelLiquidity::new(duration_since_epoch))
1761						.as_directed_mut(source, &target, capacity_msat)
1762						.failed_downstream(amount_msat, duration_since_epoch,
1763							format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
1764				}
1765			} else {
1766				log_debug!(self.logger, "Not able to penalize channel with SCID {} as we do not have graph info for it (likely a route-hint last-hop).",
1767					hop.short_channel_id);
1768			}
1769			if at_failed_channel { break; }
1770		}
1771		self.last_update_time = duration_since_epoch;
1772	}
1773
1774	#[rustfmt::skip]
1775	fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
1776		let amount_msat = path.final_value_msat();
1777		log_trace!(self.logger, "Scoring path through SCID {} as having succeeded at {} msat.",
1778			path.hops.split_last().map(|(hop, _)| hop.short_channel_id).unwrap_or(0), amount_msat);
1779		let network_graph = self.network_graph.read_only();
1780		for hop in &path.hops {
1781			let target = NodeId::from_pubkey(&hop.pubkey);
1782			let channel_directed_from_source = network_graph.channels()
1783				.get(&hop.short_channel_id)
1784				.and_then(|channel| channel.as_directed_to(&target));
1785
1786			// Only score announced channels.
1787			if let Some((channel, source)) = channel_directed_from_source {
1788				let capacity_msat = channel.effective_capacity().as_msat();
1789				self.channel_liquidities
1790					.entry(hop.short_channel_id)
1791					.or_insert_with(|| ChannelLiquidity::new(duration_since_epoch))
1792					.as_directed_mut(source, &target, capacity_msat)
1793					.successful(amount_msat, duration_since_epoch,
1794						format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
1795			} else {
1796				log_debug!(self.logger, "Not able to learn for channel with SCID {} as we do not have graph info for it (likely a route-hint last-hop).",
1797					hop.short_channel_id);
1798			}
1799		}
1800		self.last_update_time = duration_since_epoch;
1801	}
1802
1803	fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
1804		self.payment_path_failed(path, short_channel_id, duration_since_epoch)
1805	}
1806
1807	fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
1808		self.payment_path_failed(path, u64::max_value(), duration_since_epoch)
1809	}
1810
1811	fn time_passed(&mut self, duration_since_epoch: Duration) {
1812		self.channel_liquidities.time_passed(duration_since_epoch, self.decay_params);
1813		self.last_update_time = duration_since_epoch;
1814	}
1815}
1816
1817/// A probabilistic scorer that combines local and external information to score channels. This scorer is
1818/// shadow-tracking local only scores, so that it becomes possible to cleanly merge external scores when they become
1819/// available.
1820///
1821/// This is useful for nodes that have a limited local view of the network and need to augment their view with scores
1822/// from an external source to improve payment reliability. The external source may use something like background
1823/// probing to gather a more complete view of the network. Merging reduces the likelihood of losing unique local data on
1824/// particular channels.
1825///
1826/// Note that only the locally acquired data is persisted. After a restart, the external scores will be lost and must be
1827/// resupplied.
1828pub struct CombinedScorer<G: Deref<Target = NetworkGraph<L>>, L: Deref>
1829where
1830	L::Target: Logger,
1831{
1832	local_only_scorer: ProbabilisticScorer<G, L>,
1833	scorer: ProbabilisticScorer<G, L>,
1834}
1835
1836impl<G: Deref<Target = NetworkGraph<L>> + Clone, L: Deref + Clone> CombinedScorer<G, L>
1837where
1838	L::Target: Logger,
1839{
1840	/// Create a new combined scorer with the given local scorer.
1841	#[rustfmt::skip]
1842	pub fn new(local_scorer: ProbabilisticScorer<G, L>) -> Self {
1843		let decay_params = local_scorer.decay_params;
1844		let network_graph = local_scorer.network_graph.clone();
1845		let logger = local_scorer.logger.clone();
1846		let mut scorer = ProbabilisticScorer::new(decay_params, network_graph, logger);
1847
1848		scorer.channel_liquidities = local_scorer.channel_liquidities.clone();
1849
1850		Self {
1851			local_only_scorer: local_scorer,
1852			scorer: scorer,
1853		}
1854	}
1855
1856	/// Merge external channel liquidity information into the scorer.
1857	pub fn merge(
1858		&mut self, mut external_scores: ChannelLiquidities, duration_since_epoch: Duration,
1859	) {
1860		// Decay both sets of scores to make them comparable and mergeable.
1861		self.local_only_scorer.time_passed(duration_since_epoch);
1862		external_scores.time_passed(duration_since_epoch, self.local_only_scorer.decay_params);
1863
1864		let local_scores = &self.local_only_scorer.channel_liquidities;
1865
1866		// For each channel, merge the external liquidity information with the isolated local liquidity information.
1867		for (scid, mut liquidity) in external_scores.0 {
1868			if let Some(local_liquidity) = local_scores.get(&scid) {
1869				liquidity.merge(local_liquidity);
1870			}
1871			self.scorer.channel_liquidities.insert(scid, liquidity);
1872		}
1873	}
1874
1875	/// Overwrite the scorer state with the given external scores.
1876	pub fn set_scores(&mut self, external_scores: ChannelLiquidities) {
1877		self.scorer.set_scores(external_scores);
1878	}
1879}
1880
1881impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreLookUp for CombinedScorer<G, L>
1882where
1883	L::Target: Logger,
1884{
1885	type ScoreParams = ProbabilisticScoringFeeParameters;
1886
1887	fn channel_penalty_msat(
1888		&self, candidate: &CandidateRouteHop, usage: ChannelUsage,
1889		score_params: &ProbabilisticScoringFeeParameters,
1890	) -> u64 {
1891		self.scorer.channel_penalty_msat(candidate, usage, score_params)
1892	}
1893}
1894
1895impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> ScoreUpdate for CombinedScorer<G, L>
1896where
1897	L::Target: Logger,
1898{
1899	fn payment_path_failed(
1900		&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration,
1901	) {
1902		self.local_only_scorer.payment_path_failed(path, short_channel_id, duration_since_epoch);
1903		self.scorer.payment_path_failed(path, short_channel_id, duration_since_epoch);
1904	}
1905
1906	fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
1907		self.local_only_scorer.payment_path_successful(path, duration_since_epoch);
1908		self.scorer.payment_path_successful(path, duration_since_epoch);
1909	}
1910
1911	fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration) {
1912		self.local_only_scorer.probe_failed(path, short_channel_id, duration_since_epoch);
1913		self.scorer.probe_failed(path, short_channel_id, duration_since_epoch);
1914	}
1915
1916	fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration) {
1917		self.local_only_scorer.probe_successful(path, duration_since_epoch);
1918		self.scorer.probe_successful(path, duration_since_epoch);
1919	}
1920
1921	fn time_passed(&mut self, duration_since_epoch: Duration) {
1922		self.local_only_scorer.time_passed(duration_since_epoch);
1923		self.scorer.time_passed(duration_since_epoch);
1924	}
1925}
1926
1927impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> Writeable for CombinedScorer<G, L>
1928where
1929	L::Target: Logger,
1930{
1931	fn write<W: crate::util::ser::Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
1932		self.local_only_scorer.write(writer)
1933	}
1934}
1935
1936#[cfg(c_bindings)]
1937impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> Score for ProbabilisticScorer<G, L> where
1938	L::Target: Logger
1939{
1940}
1941
1942#[cfg(feature = "std")]
1943#[inline]
1944fn powf64(n: f64, exp: f64) -> f64 {
1945	n.powf(exp)
1946}
1947#[cfg(not(feature = "std"))]
1948fn powf64(n: f64, exp: f64) -> f64 {
1949	libm::pow(n, exp)
1950}
1951
1952mod bucketed_history {
1953	use super::*;
1954
1955	// Because liquidity is often skewed heavily in one direction, we store historical state
1956	// distribution in buckets of different size. For backwards compatibility, buckets of size 1/8th
1957	// must fit evenly into the buckets here.
1958	//
1959	// The smallest bucket is 2^-14th of the channel, for each of our 32 buckets here we define the
1960	// width of the bucket in 2^14'ths of the channel. This increases exponentially until we reach
1961	// a full 16th of the channel's capacity, which is reapeated a few times for backwards
1962	// compatibility. The four middle buckets represent full octiles of the channel's capacity.
1963	//
1964	// For a 1 BTC channel, this let's us differentiate between failures in the bottom 6k sats, or
1965	// between the 12,000th sat and 24,000th sat, while only needing to store and operate on 32
1966	// buckets in total.
1967
1968	// By default u16s may not be cache-aligned, but we'd rather not have to read a third cache
1969	// line just to access it
1970	#[repr(align(128))]
1971	struct BucketStartPos([u16; 33]);
1972	impl BucketStartPos {
1973		const fn new() -> Self {
1974			Self([
1975				0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 3072, 4096, 6144, 8192,
1976				10240, 12288, 13312, 14336, 15360, 15872, 16128, 16256, 16320, 16352, 16368, 16376,
1977				16380, 16382, 16383, 16384,
1978			])
1979		}
1980	}
1981	impl core::ops::Index<usize> for BucketStartPos {
1982		type Output = u16;
1983		#[inline(always)]
1984		#[rustfmt::skip]
1985		fn index(&self, index: usize) -> &u16 { &self.0[index] }
1986	}
1987	const BUCKET_START_POS: BucketStartPos = BucketStartPos::new();
1988
1989	const LEGACY_TO_BUCKET_RANGE: [(u8, u8); 8] =
1990		[(0, 12), (12, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 20), (20, 32)];
1991
1992	const POSITION_TICKS: u16 = 1 << 14;
1993
1994	fn pos_to_bucket(pos: u16) -> usize {
1995		for bucket in 0..32 {
1996			if pos < BUCKET_START_POS[bucket + 1] {
1997				return bucket;
1998			}
1999		}
2000		debug_assert!(false);
2001		return 32;
2002	}
2003
2004	#[cfg(test)]
2005	#[test]
2006	#[rustfmt::skip]
2007	fn check_bucket_maps() {
2008		const BUCKET_WIDTH_IN_16384S: [u16; 32] = [
2009			1, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024, 1024, 2048, 2048,
2010			2048, 2048, 1024, 1024, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 1];
2011
2012		let mut min_size_iter = 0;
2013		let mut legacy_bucket_iter = 0;
2014		for (bucket, width) in BUCKET_WIDTH_IN_16384S.iter().enumerate() {
2015			assert_eq!(BUCKET_START_POS[bucket], min_size_iter);
2016			for i in 0..*width {
2017				assert_eq!(pos_to_bucket(min_size_iter + i) as usize, bucket);
2018			}
2019			min_size_iter += *width;
2020			if min_size_iter % (POSITION_TICKS / 8) == 0 {
2021				assert_eq!(LEGACY_TO_BUCKET_RANGE[legacy_bucket_iter].1 as usize, bucket + 1);
2022				if legacy_bucket_iter + 1 < 8 {
2023					assert_eq!(LEGACY_TO_BUCKET_RANGE[legacy_bucket_iter + 1].0 as usize, bucket + 1);
2024				}
2025				legacy_bucket_iter += 1;
2026			}
2027		}
2028		assert_eq!(BUCKET_START_POS[32], POSITION_TICKS);
2029		assert_eq!(min_size_iter, POSITION_TICKS);
2030	}
2031
2032	#[inline]
2033	#[rustfmt::skip]
2034	fn amount_to_pos(amount_msat: u64, capacity_msat: u64) -> u16 {
2035		let pos = if amount_msat < u64::max_value() / (POSITION_TICKS as u64) {
2036			(amount_msat * (POSITION_TICKS as u64) / capacity_msat.saturating_add(1))
2037				.try_into().unwrap_or(POSITION_TICKS)
2038		} else {
2039			// Only use 128-bit arithmetic when multiplication will overflow to avoid 128-bit
2040			// division. This branch should only be hit in fuzz testing since the amount would
2041			// need to be over 2.88 million BTC in practice.
2042			((amount_msat as u128) * (POSITION_TICKS as u128)
2043					/ (capacity_msat as u128).saturating_add(1))
2044				.try_into().unwrap_or(POSITION_TICKS)
2045		};
2046		// If we are running in a client that doesn't validate gossip, its possible for a channel's
2047		// capacity to change due to a `channel_update` message which, if received while a payment
2048		// is in-flight, could cause this to fail. Thus, we only assert in test.
2049		#[cfg(test)]
2050		debug_assert!(pos < POSITION_TICKS);
2051		pos
2052	}
2053
2054	/// Prior to LDK 0.0.117 we used eight buckets which were split evenly across the either
2055	/// octiles. This was changed to use 32 buckets for accuracy reasons in 0.0.117, however we
2056	/// support reading the legacy values here for backwards compatibility.
2057	pub(super) struct LegacyHistoricalBucketRangeTracker {
2058		buckets: [u16; 8],
2059	}
2060
2061	impl LegacyHistoricalBucketRangeTracker {
2062		pub(crate) fn into_current(self) -> HistoricalBucketRangeTracker {
2063			let mut buckets = [0; 32];
2064			for (idx, legacy_bucket) in self.buckets.iter().enumerate() {
2065				let mut new_val = *legacy_bucket;
2066				let (start, end) = LEGACY_TO_BUCKET_RANGE[idx];
2067				new_val /= (end - start) as u16;
2068				for i in start..end {
2069					buckets[i as usize] = new_val;
2070				}
2071			}
2072			HistoricalBucketRangeTracker { buckets }
2073		}
2074	}
2075
2076	/// Tracks the historical state of a distribution as a weighted average of how much time was spent
2077	/// in each of 32 buckets.
2078	#[derive(Clone, Copy)]
2079	pub(super) struct HistoricalBucketRangeTracker {
2080		buckets: [u16; 32],
2081	}
2082
2083	/// Buckets are stored in fixed point numbers with a 5 bit fractional part. Thus, the value
2084	/// "one" is 32, or this constant.
2085	pub const BUCKET_FIXED_POINT_ONE: u16 = 32;
2086
2087	impl HistoricalBucketRangeTracker {
2088		#[rustfmt::skip]
2089		pub(super) fn new() -> Self { Self { buckets: [0; 32] } }
2090		fn track_datapoint(&mut self, liquidity_offset_msat: u64, capacity_msat: u64) {
2091			// We have 32 leaky buckets for min and max liquidity. Each bucket tracks the amount of time
2092			// we spend in each bucket as a 16-bit fixed-point number with a 5 bit fractional part.
2093			//
2094			// Each time we update our liquidity estimate, we add 32 (1.0 in our fixed-point system) to
2095			// the buckets for the current min and max liquidity offset positions.
2096			//
2097			// We then decay each bucket by multiplying by 2047/2048 (avoiding dividing by a
2098			// non-power-of-two). This ensures we can't actually overflow the u16 - when we get to
2099			// 63,457 adding 32 and decaying by 2047/2048 leaves us back at 63,457.
2100			//
2101			// In total, this allows us to track data for the last 8,000 or so payments across a given
2102			// channel.
2103			//
2104			// These constants are a balance - we try to fit in 2 bytes per bucket to reduce overhead,
2105			// and need to balance having more bits in the decimal part (to ensure decay isn't too
2106			// non-linear) with having too few bits in the mantissa, causing us to not store very many
2107			// datapoints.
2108			//
2109			// The constants were picked experimentally, selecting a decay amount that restricts us
2110			// from overflowing buckets without having to cap them manually.
2111
2112			let pos: u16 = amount_to_pos(liquidity_offset_msat, capacity_msat);
2113			if pos < POSITION_TICKS {
2114				for e in self.buckets.iter_mut() {
2115					*e = ((*e as u32) * 2047 / 2048) as u16;
2116				}
2117				let bucket = pos_to_bucket(pos);
2118				self.buckets[bucket] = self.buckets[bucket].saturating_add(BUCKET_FIXED_POINT_ONE);
2119			}
2120		}
2121
2122		/// Returns the average of the buckets between the two trackers.
2123		pub(crate) fn merge(&mut self, other: &Self) -> () {
2124			for (bucket, other_bucket) in self.buckets.iter_mut().zip(other.buckets.iter()) {
2125				*bucket = ((*bucket as u32 + *other_bucket as u32) / 2) as u16;
2126			}
2127		}
2128
2129		/// Applies decay at the given half-life to all buckets.
2130		fn decay(&mut self, half_lives: f64) {
2131			let factor = (1024.0 * powf64(0.5, half_lives)) as u64;
2132			for bucket in self.buckets.iter_mut() {
2133				*bucket = ((*bucket as u64) * factor / 1024) as u16;
2134			}
2135		}
2136	}
2137
2138	impl_writeable_tlv_based!(HistoricalBucketRangeTracker, { (0, buckets, required) });
2139	impl_writeable_tlv_based!(LegacyHistoricalBucketRangeTracker, { (0, buckets, required) });
2140
2141	#[derive(Clone, Copy)]
2142	#[repr(C)] // Force the fields in memory to be in the order we specify.
2143	pub(super) struct HistoricalLiquidityTracker {
2144		// This struct sits inside a `(u64, ChannelLiquidity)` in memory, and we first read the
2145		// liquidity offsets in `ChannelLiquidity` when calculating the non-historical score. This
2146		// means that the first handful of bytes of this struct will already be sitting in cache by
2147		// the time we go to look at them.
2148		//
2149		// Because the first thing we do is check if `total_valid_points` is sufficient to consider
2150		// the data here at all, and can return early if it is not, we want this to go first to
2151		// avoid hitting a second cache line load entirely in that case.
2152		//
2153		// Note that we store it as an `f64` rather than a `u64` (potentially losing some
2154		// precision) because we ultimately need the value as an `f64` when dividing bucket weights
2155		// by it. Storing it as an `f64` avoids doing the additional int -> float conversion in the
2156		// hot score-calculation path.
2157		total_valid_points_tracked: f64,
2158		min_liquidity_offset_history: HistoricalBucketRangeTracker,
2159		max_liquidity_offset_history: HistoricalBucketRangeTracker,
2160	}
2161
2162	impl HistoricalLiquidityTracker {
2163		pub(super) fn new() -> HistoricalLiquidityTracker {
2164			HistoricalLiquidityTracker {
2165				min_liquidity_offset_history: HistoricalBucketRangeTracker::new(),
2166				max_liquidity_offset_history: HistoricalBucketRangeTracker::new(),
2167				total_valid_points_tracked: 0.0,
2168			}
2169		}
2170
2171		pub(super) fn from_min_max(
2172			min_liquidity_offset_history: HistoricalBucketRangeTracker,
2173			max_liquidity_offset_history: HistoricalBucketRangeTracker,
2174		) -> HistoricalLiquidityTracker {
2175			let mut res = HistoricalLiquidityTracker {
2176				min_liquidity_offset_history,
2177				max_liquidity_offset_history,
2178				total_valid_points_tracked: 0.0,
2179			};
2180			res.recalculate_valid_point_count();
2181			res
2182		}
2183
2184		#[rustfmt::skip]
2185		pub(super) fn has_datapoints(&self) -> bool {
2186			self.min_liquidity_offset_history.buckets != [0; 32] ||
2187				self.max_liquidity_offset_history.buckets != [0; 32]
2188		}
2189
2190		pub(super) fn decay_buckets(&mut self, half_lives: f64) {
2191			self.min_liquidity_offset_history.decay(half_lives);
2192			self.max_liquidity_offset_history.decay(half_lives);
2193			self.recalculate_valid_point_count();
2194		}
2195
2196		#[rustfmt::skip]
2197		fn recalculate_valid_point_count(&mut self) {
2198			let mut total_valid_points_tracked = 0u128;
2199			for (min_idx, min_bucket) in self.min_liquidity_offset_history.buckets.iter().enumerate() {
2200				for max_bucket in self.max_liquidity_offset_history.buckets.iter().take(32 - min_idx) {
2201					// In testing, raising the weights of buckets to a high power led to better
2202					// scoring results. Thus, we raise the bucket weights to the 4th power here (by
2203					// squaring the result of multiplying the weights). This results in
2204					// bucket_weight having at max 64 bits, which means we have to do our summation
2205					// in 128-bit math.
2206					let mut bucket_weight = (*min_bucket as u64) * (*max_bucket as u64);
2207					bucket_weight *= bucket_weight;
2208					total_valid_points_tracked += bucket_weight as u128;
2209				}
2210			}
2211			self.total_valid_points_tracked = total_valid_points_tracked as f64;
2212		}
2213
2214		pub(super) fn writeable_min_offset_history(&self) -> &HistoricalBucketRangeTracker {
2215			&self.min_liquidity_offset_history
2216		}
2217
2218		pub(super) fn writeable_max_offset_history(&self) -> &HistoricalBucketRangeTracker {
2219			&self.max_liquidity_offset_history
2220		}
2221
2222		pub(super) fn as_directed<'a>(
2223			&'a self, source_less_than_target: bool,
2224		) -> DirectedHistoricalLiquidityTracker<&'a HistoricalLiquidityTracker> {
2225			DirectedHistoricalLiquidityTracker { source_less_than_target, tracker: self }
2226		}
2227
2228		pub(super) fn as_directed_mut<'a>(
2229			&'a mut self, source_less_than_target: bool,
2230		) -> DirectedHistoricalLiquidityTracker<&'a mut HistoricalLiquidityTracker> {
2231			DirectedHistoricalLiquidityTracker { source_less_than_target, tracker: self }
2232		}
2233
2234		/// Merges the historical liquidity data from another tracker into this one.
2235		pub fn merge(&mut self, other: &Self) {
2236			self.min_liquidity_offset_history.merge(&other.min_liquidity_offset_history);
2237			self.max_liquidity_offset_history.merge(&other.max_liquidity_offset_history);
2238			self.recalculate_valid_point_count();
2239		}
2240	}
2241
2242	/// A set of buckets representing the history of where we've seen the minimum- and maximum-
2243	/// liquidity bounds for a given channel.
2244	pub(super) struct DirectedHistoricalLiquidityTracker<
2245		D: Deref<Target = HistoricalLiquidityTracker>,
2246	> {
2247		source_less_than_target: bool,
2248		tracker: D,
2249	}
2250
2251	impl<D: DerefMut<Target = HistoricalLiquidityTracker>> DirectedHistoricalLiquidityTracker<D> {
2252		#[rustfmt::skip]
2253		pub(super) fn track_datapoint(
2254			&mut self, min_offset_msat: u64, max_offset_msat: u64, capacity_msat: u64,
2255		) {
2256			if self.source_less_than_target {
2257				self.tracker.min_liquidity_offset_history.track_datapoint(min_offset_msat, capacity_msat);
2258				self.tracker.max_liquidity_offset_history.track_datapoint(max_offset_msat, capacity_msat);
2259			} else {
2260				self.tracker.max_liquidity_offset_history.track_datapoint(min_offset_msat, capacity_msat);
2261				self.tracker.min_liquidity_offset_history.track_datapoint(max_offset_msat, capacity_msat);
2262			}
2263			self.tracker.recalculate_valid_point_count();
2264		}
2265	}
2266
2267	impl<D: Deref<Target = HistoricalLiquidityTracker>> DirectedHistoricalLiquidityTracker<D> {
2268		pub(super) fn min_liquidity_offset_history_buckets(&self) -> &[u16; 32] {
2269			if self.source_less_than_target {
2270				&self.tracker.min_liquidity_offset_history.buckets
2271			} else {
2272				&self.tracker.max_liquidity_offset_history.buckets
2273			}
2274		}
2275
2276		pub(super) fn max_liquidity_offset_history_buckets(&self) -> &[u16; 32] {
2277			if self.source_less_than_target {
2278				&self.tracker.max_liquidity_offset_history.buckets
2279			} else {
2280				&self.tracker.min_liquidity_offset_history.buckets
2281			}
2282		}
2283
2284		#[inline]
2285		#[rustfmt::skip]
2286		pub(super) fn calculate_success_probability_times_billion(
2287			&self, params: &ProbabilisticScoringFeeParameters, total_inflight_amount_msat: u64,
2288			capacity_msat: u64
2289		) -> Option<u64> {
2290			// If historical penalties are enabled, we try to calculate a probability of success
2291			// given our historical distribution of min- and max-liquidity bounds in a channel.
2292			// To do so, we walk the set of historical liquidity bucket (min, max) combinations
2293			// (where min_idx < max_idx, as having a minimum above our maximum is an invalid
2294			// state). For each pair, we calculate the probability as if the bucket's corresponding
2295			// min- and max- liquidity bounds were our current liquidity bounds and then multiply
2296			// that probability by the weight of the selected buckets.
2297			let payment_pos = amount_to_pos(total_inflight_amount_msat, capacity_msat);
2298			if payment_pos >= POSITION_TICKS { return None; }
2299
2300			let min_liquidity_offset_history_buckets =
2301				self.min_liquidity_offset_history_buckets();
2302			let max_liquidity_offset_history_buckets =
2303				self.max_liquidity_offset_history_buckets();
2304
2305			let total_valid_points_tracked = self.tracker.total_valid_points_tracked;
2306			#[cfg(debug_assertions)] {
2307				let mut actual_valid_points_tracked = 0u128;
2308				for (min_idx, min_bucket) in min_liquidity_offset_history_buckets.iter().enumerate() {
2309					for max_bucket in max_liquidity_offset_history_buckets.iter().take(32 - min_idx) {
2310						let mut bucket_weight = (*min_bucket as u64) * (*max_bucket as u64);
2311						bucket_weight *= bucket_weight;
2312						actual_valid_points_tracked += bucket_weight as u128;
2313					}
2314				}
2315				assert_eq!(total_valid_points_tracked, actual_valid_points_tracked as f64);
2316			}
2317
2318			// If the total valid points is smaller than 1.0 (i.e. 32 in our fixed-point scheme),
2319			// treat it as if we were fully decayed.
2320			const FULLY_DECAYED: f64 = BUCKET_FIXED_POINT_ONE as f64 * BUCKET_FIXED_POINT_ONE as f64 *
2321				BUCKET_FIXED_POINT_ONE as f64 * BUCKET_FIXED_POINT_ONE as f64;
2322			if total_valid_points_tracked < FULLY_DECAYED.into() {
2323				return None;
2324			}
2325
2326			let mut cumulative_success_prob = 0.0f64;
2327			// Special-case the 0th min bucket - it generally means we failed a payment, so only
2328			// consider the highest (i.e. largest-offset-from-max-capacity) max bucket for all
2329			// points against the 0th min bucket. This avoids the case where we fail to route
2330			// increasingly lower values over a channel, but treat each failure as a separate
2331			// datapoint, many of which may have relatively high maximum-available-liquidity
2332			// values, which will result in us thinking we have some nontrivial probability of
2333			// routing up to that amount.
2334			if min_liquidity_offset_history_buckets[0] != 0 {
2335				// Track the highest max-buckets with any data at all, as well as the highest
2336				// max-bucket with at least BUCKET_FIXED_POINT_ONE.
2337				let mut highest_max_bucket_with_points = 0;
2338				let mut highest_max_bucket_with_full_points = None;
2339				let mut total_weight = 0u128;
2340				for (max_idx, max_bucket) in max_liquidity_offset_history_buckets.iter().enumerate() {
2341					if *max_bucket >= BUCKET_FIXED_POINT_ONE {
2342						highest_max_bucket_with_full_points = Some(cmp::max(highest_max_bucket_with_full_points.unwrap_or(0), max_idx));
2343					}
2344					if *max_bucket != 0 {
2345						highest_max_bucket_with_points = cmp::max(highest_max_bucket_with_points, max_idx);
2346					}
2347					// In testing, raising the weights of buckets to a high power led to better
2348					// scoring results. Thus, we raise the bucket weights to the 4th power here (by
2349					// squaring the result of multiplying the weights), matching the logic in
2350					// `recalculate_valid_point_count`.
2351					let bucket_weight = (*max_bucket as u64) * (min_liquidity_offset_history_buckets[0] as u64);
2352					total_weight += (bucket_weight * bucket_weight) as u128;
2353				}
2354				debug_assert!(total_weight as f64 <= total_valid_points_tracked);
2355				// Use the highest max-bucket with at least BUCKET_FIXED_POINT_ONE, but if none is
2356				// available use the highest max-bucket with any non-zero value. This ensures that
2357				// if we have substantially decayed data we don't end up thinking the highest
2358				// max-bucket is zero even though we have no points in the 0th max-bucket and do
2359				// have points elsewhere.
2360				let selected_max = highest_max_bucket_with_full_points.unwrap_or(highest_max_bucket_with_points);
2361				let max_bucket_end_pos = BUCKET_START_POS[32 - selected_max] - 1;
2362				if payment_pos < max_bucket_end_pos {
2363					let (numerator, denominator) = success_probability_float(payment_pos as u64, 0,
2364						max_bucket_end_pos as u64, POSITION_TICKS as u64 - 1, params, true);
2365					let bucket_prob = total_weight as f64 / total_valid_points_tracked;
2366					cumulative_success_prob += bucket_prob * numerator / denominator;
2367				}
2368			}
2369
2370			for (min_idx, min_bucket) in min_liquidity_offset_history_buckets.iter().enumerate().skip(1) {
2371				let min_bucket_start_pos = BUCKET_START_POS[min_idx];
2372				for (max_idx, max_bucket) in max_liquidity_offset_history_buckets.iter().enumerate().take(32 - min_idx) {
2373					let max_bucket_end_pos = BUCKET_START_POS[32 - max_idx] - 1;
2374					if payment_pos >= max_bucket_end_pos {
2375						// Success probability 0, the payment amount may be above the max liquidity
2376						break;
2377					}
2378
2379					// In testing, raising the weights of buckets to a high power led to better
2380					// scoring results. Thus, we raise the bucket weights to the 4th power here (by
2381					// squaring the result of multiplying the weights), matching the logic in
2382					// `recalculate_valid_point_count`.
2383					let mut bucket_weight = (*min_bucket as u64) * (*max_bucket as u64);
2384					bucket_weight *= bucket_weight;
2385					debug_assert!(bucket_weight as f64 <= total_valid_points_tracked);
2386					let bucket_prob = bucket_weight as f64 / total_valid_points_tracked;
2387
2388					if payment_pos < min_bucket_start_pos {
2389						cumulative_success_prob += bucket_prob;
2390					} else {
2391						let (numerator, denominator) = success_probability_float(payment_pos as u64,
2392							min_bucket_start_pos as u64, max_bucket_end_pos as u64,
2393							POSITION_TICKS as u64 - 1, params, true);
2394						cumulative_success_prob += bucket_prob * numerator / denominator;
2395					}
2396				}
2397			}
2398
2399			Some((cumulative_success_prob * (1024.0 * 1024.0 * 1024.0)) as u64)
2400		}
2401	}
2402
2403	#[cfg(test)]
2404	mod tests {
2405		use crate::routing::scoring::ProbabilisticScoringFeeParameters;
2406
2407		use super::{HistoricalBucketRangeTracker, HistoricalLiquidityTracker};
2408		#[test]
2409		fn historical_liquidity_bucket_merge() {
2410			let mut bucket1 = HistoricalBucketRangeTracker::new();
2411			bucket1.track_datapoint(100, 1000);
2412			assert_eq!(
2413				bucket1.buckets,
2414				[
2415					0u16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2416					0, 0, 0, 0, 0, 0, 0
2417				]
2418			);
2419
2420			let mut bucket2 = HistoricalBucketRangeTracker::new();
2421			bucket2.track_datapoint(0, 1000);
2422			assert_eq!(
2423				bucket2.buckets,
2424				[
2425					32u16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2426					0, 0, 0, 0, 0, 0, 0
2427				]
2428			);
2429
2430			bucket1.merge(&bucket2);
2431			assert_eq!(
2432				bucket1.buckets,
2433				[
2434					16u16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2435					0, 0, 0, 0, 0, 0, 0
2436				]
2437			);
2438		}
2439
2440		#[test]
2441		fn historical_liquidity_bucket_decay() {
2442			let mut bucket = HistoricalBucketRangeTracker::new();
2443			bucket.track_datapoint(100, 1000);
2444			assert_eq!(
2445				bucket.buckets,
2446				[
2447					0u16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2448					0, 0, 0, 0, 0, 0, 0
2449				]
2450			);
2451
2452			bucket.decay(2.0);
2453			assert_eq!(
2454				bucket.buckets,
2455				[
2456					0u16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2457					0, 0, 0, 0, 0, 0, 0
2458				]
2459			);
2460		}
2461
2462		#[test]
2463		fn historical_liquidity_tracker_merge() {
2464			let params = ProbabilisticScoringFeeParameters::default();
2465
2466			let probability1: Option<u64>;
2467			let mut tracker1 = HistoricalLiquidityTracker::new();
2468			{
2469				let mut directed_tracker1 = tracker1.as_directed_mut(true);
2470				directed_tracker1.track_datapoint(100, 200, 1000);
2471				probability1 = directed_tracker1
2472					.calculate_success_probability_times_billion(&params, 500, 1000);
2473			}
2474
2475			let mut tracker2 = HistoricalLiquidityTracker::new();
2476			{
2477				let mut directed_tracker2 = tracker2.as_directed_mut(true);
2478				directed_tracker2.track_datapoint(200, 300, 1000);
2479			}
2480
2481			tracker1.merge(&tracker2);
2482
2483			let directed_tracker1 = tracker1.as_directed(true);
2484			let probability =
2485				directed_tracker1.calculate_success_probability_times_billion(&params, 500, 1000);
2486
2487			assert_ne!(probability1, probability);
2488		}
2489
2490		#[test]
2491		fn historical_heavy_buckets_operations() {
2492			// Checks that we don't hit overflows when working with tons of data (even an
2493			// impossible-to-reach amount of data).
2494			let mut tracker = HistoricalLiquidityTracker::new();
2495			tracker.min_liquidity_offset_history.buckets = [0xffff; 32];
2496			tracker.max_liquidity_offset_history.buckets = [0xffff; 32];
2497			tracker.recalculate_valid_point_count();
2498			tracker.merge(&tracker.clone());
2499			assert_eq!(tracker.min_liquidity_offset_history.buckets, [0xffff; 32]);
2500			assert_eq!(tracker.max_liquidity_offset_history.buckets, [0xffff; 32]);
2501
2502			let mut directed = tracker.as_directed_mut(true);
2503			let default_params = ProbabilisticScoringFeeParameters::default();
2504			directed.calculate_success_probability_times_billion(&default_params, 42, 1000);
2505			directed.track_datapoint(42, 52, 1000);
2506
2507			tracker.decay_buckets(1.0);
2508		}
2509	}
2510}
2511
2512impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> Writeable for ProbabilisticScorer<G, L>
2513where
2514	L::Target: Logger,
2515{
2516	#[inline]
2517	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
2518		self.channel_liquidities.write(w)
2519	}
2520}
2521
2522impl<G: Deref<Target = NetworkGraph<L>>, L: Deref>
2523	ReadableArgs<(ProbabilisticScoringDecayParameters, G, L)> for ProbabilisticScorer<G, L>
2524where
2525	L::Target: Logger,
2526{
2527	#[inline]
2528	#[rustfmt::skip]
2529	fn read<R: Read>(
2530		r: &mut R, args: (ProbabilisticScoringDecayParameters, G, L)
2531	) -> Result<Self, DecodeError> {
2532		let (decay_params, network_graph, logger) = args;
2533		let channel_liquidities = ChannelLiquidities::read(r)?;
2534		let mut last_update_time = Duration::from_secs(0);
2535		for (_, liq) in channel_liquidities.0.iter() {
2536			last_update_time = cmp::max(last_update_time, liq.last_updated);
2537		}
2538		Ok(Self {
2539			decay_params,
2540			network_graph,
2541			logger,
2542			channel_liquidities,
2543			last_update_time,
2544		})
2545	}
2546}
2547
2548impl Writeable for ChannelLiquidity {
2549	#[inline]
2550	fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
2551		write_tlv_fields!(w, {
2552			(0, self.min_liquidity_offset_msat, required),
2553			// 1 was the min_liquidity_offset_history in octile form
2554			(2, self.max_liquidity_offset_msat, required),
2555			// 3 was the max_liquidity_offset_history in octile form
2556			(4, self.last_updated, required),
2557			(5, self.liquidity_history.writeable_min_offset_history(), required),
2558			(7, self.liquidity_history.writeable_max_offset_history(), required),
2559			(9, self.offset_history_last_updated, required),
2560			(11, self.last_datapoint_time, required),
2561		});
2562		Ok(())
2563	}
2564}
2565
2566impl Readable for ChannelLiquidity {
2567	#[inline]
2568	#[rustfmt::skip]
2569	fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
2570		let mut min_liquidity_offset_msat = 0;
2571		let mut max_liquidity_offset_msat = 0;
2572		let mut legacy_min_liq_offset_history: Option<LegacyHistoricalBucketRangeTracker> = None;
2573		let mut legacy_max_liq_offset_history: Option<LegacyHistoricalBucketRangeTracker> = None;
2574		let mut min_liquidity_offset_history: Option<HistoricalBucketRangeTracker> = None;
2575		let mut max_liquidity_offset_history: Option<HistoricalBucketRangeTracker> = None;
2576		let mut last_updated = Duration::from_secs(0);
2577		let mut offset_history_last_updated = None;
2578		let mut last_datapoint_time = None;
2579		read_tlv_fields!(r, {
2580			(0, min_liquidity_offset_msat, required),
2581			(1, legacy_min_liq_offset_history, option),
2582			(2, max_liquidity_offset_msat, required),
2583			(3, legacy_max_liq_offset_history, option),
2584			(4, last_updated, required),
2585			(5, min_liquidity_offset_history, option),
2586			(7, max_liquidity_offset_history, option),
2587			(9, offset_history_last_updated, option),
2588			(11, last_datapoint_time, option),
2589		});
2590
2591		if min_liquidity_offset_history.is_none() {
2592			if let Some(legacy_buckets) = legacy_min_liq_offset_history {
2593				min_liquidity_offset_history = Some(legacy_buckets.into_current());
2594			} else {
2595				min_liquidity_offset_history = Some(HistoricalBucketRangeTracker::new());
2596			}
2597		}
2598		if max_liquidity_offset_history.is_none() {
2599			if let Some(legacy_buckets) = legacy_max_liq_offset_history {
2600				max_liquidity_offset_history = Some(legacy_buckets.into_current());
2601			} else {
2602				max_liquidity_offset_history = Some(HistoricalBucketRangeTracker::new());
2603			}
2604		}
2605		Ok(Self {
2606			min_liquidity_offset_msat,
2607			max_liquidity_offset_msat,
2608			liquidity_history: HistoricalLiquidityTracker::from_min_max(
2609				min_liquidity_offset_history.unwrap(), max_liquidity_offset_history.unwrap()
2610			),
2611			last_updated,
2612			offset_history_last_updated: offset_history_last_updated.unwrap_or(last_updated),
2613			last_datapoint_time: last_datapoint_time.unwrap_or(last_updated),
2614		})
2615	}
2616}
2617
2618#[cfg(test)]
2619mod tests {
2620	use super::{
2621		ChannelLiquidity, HistoricalLiquidityTracker, ProbabilisticScorer,
2622		ProbabilisticScoringDecayParameters, ProbabilisticScoringFeeParameters,
2623	};
2624	use crate::blinded_path::BlindedHop;
2625	use crate::util::config::UserConfig;
2626
2627	use crate::ln::channelmanager;
2628	use crate::ln::msgs::{
2629		ChannelAnnouncement, ChannelUpdate, UnsignedChannelAnnouncement, UnsignedChannelUpdate,
2630	};
2631	use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId};
2632	use crate::routing::router::{
2633		BlindedTail, CandidateRouteHop, Path, PublicHopCandidate, RouteHop,
2634	};
2635	use crate::routing::scoring::{
2636		ChannelLiquidities, ChannelUsage, CombinedScorer, ScoreLookUp, ScoreUpdate,
2637	};
2638	use crate::util::ser::{ReadableArgs, Writeable};
2639	use crate::util::test_utils::{self, TestLogger};
2640
2641	use crate::io;
2642	use bitcoin::constants::ChainHash;
2643	use bitcoin::hashes::sha256d::Hash as Sha256dHash;
2644	use bitcoin::hashes::Hash;
2645	use bitcoin::network::Network;
2646	use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
2647	use core::time::Duration;
2648	use std::rc::Rc;
2649
2650	fn source_privkey() -> SecretKey {
2651		SecretKey::from_slice(&[42; 32]).unwrap()
2652	}
2653
2654	fn target_privkey() -> SecretKey {
2655		SecretKey::from_slice(&[43; 32]).unwrap()
2656	}
2657
2658	fn source_pubkey() -> PublicKey {
2659		let secp_ctx = Secp256k1::new();
2660		PublicKey::from_secret_key(&secp_ctx, &source_privkey())
2661	}
2662
2663	fn target_pubkey() -> PublicKey {
2664		let secp_ctx = Secp256k1::new();
2665		PublicKey::from_secret_key(&secp_ctx, &target_privkey())
2666	}
2667
2668	fn source_node_id() -> NodeId {
2669		NodeId::from_pubkey(&source_pubkey())
2670	}
2671
2672	fn target_node_id() -> NodeId {
2673		NodeId::from_pubkey(&target_pubkey())
2674	}
2675
2676	// `ProbabilisticScorer` tests
2677
2678	fn sender_privkey() -> SecretKey {
2679		SecretKey::from_slice(&[41; 32]).unwrap()
2680	}
2681
2682	fn recipient_privkey() -> SecretKey {
2683		SecretKey::from_slice(&[45; 32]).unwrap()
2684	}
2685
2686	fn sender_pubkey() -> PublicKey {
2687		let secp_ctx = Secp256k1::new();
2688		PublicKey::from_secret_key(&secp_ctx, &sender_privkey())
2689	}
2690
2691	fn recipient_pubkey() -> PublicKey {
2692		let secp_ctx = Secp256k1::new();
2693		PublicKey::from_secret_key(&secp_ctx, &recipient_privkey())
2694	}
2695
2696	fn recipient_node_id() -> NodeId {
2697		NodeId::from_pubkey(&recipient_pubkey())
2698	}
2699
2700	fn network_graph(logger: &TestLogger) -> NetworkGraph<&TestLogger> {
2701		let mut network_graph = NetworkGraph::new(Network::Testnet, logger);
2702		add_channel(&mut network_graph, 42, source_privkey(), target_privkey());
2703		add_channel(&mut network_graph, 43, target_privkey(), recipient_privkey());
2704
2705		network_graph
2706	}
2707
2708	#[rustfmt::skip]
2709	fn add_channel(
2710		network_graph: &mut NetworkGraph<&TestLogger>, short_channel_id: u64, node_1_key: SecretKey,
2711		node_2_key: SecretKey
2712	) {
2713		let genesis_hash = ChainHash::using_genesis_block(Network::Testnet);
2714		let node_1_secret = &SecretKey::from_slice(&[39; 32]).unwrap();
2715		let node_2_secret = &SecretKey::from_slice(&[40; 32]).unwrap();
2716		let secp_ctx = Secp256k1::new();
2717		let unsigned_announcement = UnsignedChannelAnnouncement {
2718			features: channelmanager::provided_channel_features(&UserConfig::default()),
2719			chain_hash: genesis_hash,
2720			short_channel_id,
2721			node_id_1: NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_1_key)),
2722			node_id_2: NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_2_key)),
2723			bitcoin_key_1: NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_1_secret)),
2724			bitcoin_key_2: NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_2_secret)),
2725			excess_data: Vec::new(),
2726		};
2727		let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
2728		let signed_announcement = ChannelAnnouncement {
2729			node_signature_1: secp_ctx.sign_ecdsa(&msghash, &node_1_key),
2730			node_signature_2: secp_ctx.sign_ecdsa(&msghash, &node_2_key),
2731			bitcoin_signature_1: secp_ctx.sign_ecdsa(&msghash, &node_1_secret),
2732			bitcoin_signature_2: secp_ctx.sign_ecdsa(&msghash, &node_2_secret),
2733			contents: unsigned_announcement,
2734		};
2735		let chain_source: Option<&crate::util::test_utils::TestChainSource> = None;
2736		network_graph.update_channel_from_announcement(
2737			&signed_announcement, &chain_source).unwrap();
2738		update_channel(network_graph, short_channel_id, node_1_key, 0, 1_000, 100);
2739		update_channel(network_graph, short_channel_id, node_2_key, 1, 0, 100);
2740	}
2741
2742	fn update_channel(
2743		network_graph: &mut NetworkGraph<&TestLogger>, short_channel_id: u64, node_key: SecretKey,
2744		channel_flags: u8, htlc_maximum_msat: u64, timestamp: u32,
2745	) {
2746		let genesis_hash = ChainHash::using_genesis_block(Network::Testnet);
2747		let secp_ctx = Secp256k1::new();
2748		let unsigned_update = UnsignedChannelUpdate {
2749			chain_hash: genesis_hash,
2750			short_channel_id,
2751			timestamp,
2752			message_flags: 1, // Only must_be_one
2753			channel_flags,
2754			cltv_expiry_delta: 18,
2755			htlc_minimum_msat: 0,
2756			htlc_maximum_msat,
2757			fee_base_msat: 1,
2758			fee_proportional_millionths: 0,
2759			excess_data: Vec::new(),
2760		};
2761		let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_update.encode()[..])[..]);
2762		let signed_update = ChannelUpdate {
2763			signature: secp_ctx.sign_ecdsa(&msghash, &node_key),
2764			contents: unsigned_update,
2765		};
2766		network_graph.update_channel(&signed_update).unwrap();
2767	}
2768
2769	fn path_hop(pubkey: PublicKey, short_channel_id: u64, fee_msat: u64) -> RouteHop {
2770		let config = UserConfig::default();
2771		RouteHop {
2772			pubkey,
2773			node_features: channelmanager::provided_node_features(&config),
2774			short_channel_id,
2775			channel_features: channelmanager::provided_channel_features(&config),
2776			fee_msat,
2777			cltv_expiry_delta: 18,
2778			maybe_announced_channel: true,
2779		}
2780	}
2781
2782	#[rustfmt::skip]
2783	fn payment_path_for_amount(amount_msat: u64) -> Path {
2784		Path {
2785			hops: vec![
2786				path_hop(source_pubkey(), 41, 1),
2787				path_hop(target_pubkey(), 42, 2),
2788				path_hop(recipient_pubkey(), 43, amount_msat),
2789			], blinded_tail: None,
2790		}
2791	}
2792
2793	#[test]
2794	#[rustfmt::skip]
2795	fn liquidity_bounds_directed_from_lowest_node_id() {
2796		let logger = TestLogger::new();
2797		let last_updated = Duration::ZERO;
2798		let offset_history_last_updated = Duration::ZERO;
2799		let last_datapoint_time = Duration::ZERO;
2800		let network_graph = network_graph(&logger);
2801		let decay_params = ProbabilisticScoringDecayParameters::default();
2802		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
2803			.with_channel(42,
2804				ChannelLiquidity {
2805					min_liquidity_offset_msat: 700, max_liquidity_offset_msat: 100,
2806					last_updated, offset_history_last_updated, last_datapoint_time,
2807					liquidity_history: HistoricalLiquidityTracker::new(),
2808				})
2809			.with_channel(43,
2810				ChannelLiquidity {
2811					min_liquidity_offset_msat: 700, max_liquidity_offset_msat: 100,
2812					last_updated, offset_history_last_updated, last_datapoint_time,
2813					liquidity_history: HistoricalLiquidityTracker::new(),
2814				});
2815		let source = source_node_id();
2816		let target = target_node_id();
2817		let recipient = recipient_node_id();
2818		assert!(source > target);
2819		assert!(target < recipient);
2820
2821		// Update minimum liquidity.
2822
2823		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2824			.as_directed(&source, &target, 1_000);
2825		assert_eq!(liquidity.min_liquidity_msat(), 100);
2826		assert_eq!(liquidity.max_liquidity_msat(), 300);
2827
2828		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2829			.as_directed(&target, &source, 1_000);
2830		assert_eq!(liquidity.min_liquidity_msat(), 700);
2831		assert_eq!(liquidity.max_liquidity_msat(), 900);
2832
2833		scorer.channel_liquidities.get_mut(&42).unwrap()
2834			.as_directed_mut(&source, &target, 1_000)
2835			.set_min_liquidity_msat(200, Duration::ZERO);
2836
2837		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2838			.as_directed(&source, &target, 1_000);
2839		assert_eq!(liquidity.min_liquidity_msat(), 200);
2840		assert_eq!(liquidity.max_liquidity_msat(), 300);
2841
2842		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2843			.as_directed(&target, &source, 1_000);
2844		assert_eq!(liquidity.min_liquidity_msat(), 700);
2845		assert_eq!(liquidity.max_liquidity_msat(), 800);
2846
2847		// Update maximum liquidity.
2848
2849		let liquidity = scorer.channel_liquidities.get(&43).unwrap()
2850			.as_directed(&target, &recipient, 1_000);
2851		assert_eq!(liquidity.min_liquidity_msat(), 700);
2852		assert_eq!(liquidity.max_liquidity_msat(), 900);
2853
2854		let liquidity = scorer.channel_liquidities.get(&43).unwrap()
2855			.as_directed(&recipient, &target, 1_000);
2856		assert_eq!(liquidity.min_liquidity_msat(), 100);
2857		assert_eq!(liquidity.max_liquidity_msat(), 300);
2858
2859		scorer.channel_liquidities.get_mut(&43).unwrap()
2860			.as_directed_mut(&target, &recipient, 1_000)
2861			.set_max_liquidity_msat(200, Duration::ZERO);
2862
2863		let liquidity = scorer.channel_liquidities.get(&43).unwrap()
2864			.as_directed(&target, &recipient, 1_000);
2865		assert_eq!(liquidity.min_liquidity_msat(), 0);
2866		assert_eq!(liquidity.max_liquidity_msat(), 200);
2867
2868		let liquidity = scorer.channel_liquidities.get(&43).unwrap()
2869			.as_directed(&recipient, &target, 1_000);
2870		assert_eq!(liquidity.min_liquidity_msat(), 800);
2871		assert_eq!(liquidity.max_liquidity_msat(), 1000);
2872	}
2873
2874	#[test]
2875	#[rustfmt::skip]
2876	fn resets_liquidity_upper_bound_when_crossed_by_lower_bound() {
2877		let logger = TestLogger::new();
2878		let last_updated = Duration::ZERO;
2879		let offset_history_last_updated = Duration::ZERO;
2880		let last_datapoint_time = Duration::ZERO;
2881		let network_graph = network_graph(&logger);
2882		let decay_params = ProbabilisticScoringDecayParameters::default();
2883		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
2884			.with_channel(42,
2885				ChannelLiquidity {
2886					min_liquidity_offset_msat: 200, max_liquidity_offset_msat: 400,
2887					last_updated, offset_history_last_updated, last_datapoint_time,
2888					liquidity_history: HistoricalLiquidityTracker::new(),
2889				});
2890		let source = source_node_id();
2891		let target = target_node_id();
2892		assert!(source > target);
2893
2894		// Check initial bounds.
2895		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2896			.as_directed(&source, &target, 1_000);
2897		assert_eq!(liquidity.min_liquidity_msat(), 400);
2898		assert_eq!(liquidity.max_liquidity_msat(), 800);
2899
2900		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2901			.as_directed(&target, &source, 1_000);
2902		assert_eq!(liquidity.min_liquidity_msat(), 200);
2903		assert_eq!(liquidity.max_liquidity_msat(), 600);
2904
2905		// Reset from source to target.
2906		scorer.channel_liquidities.get_mut(&42).unwrap()
2907			.as_directed_mut(&source, &target, 1_000)
2908			.set_min_liquidity_msat(900, Duration::ZERO);
2909
2910		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2911			.as_directed(&source, &target, 1_000);
2912		assert_eq!(liquidity.min_liquidity_msat(), 900);
2913		assert_eq!(liquidity.max_liquidity_msat(), 1_000);
2914
2915		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2916			.as_directed(&target, &source, 1_000);
2917		assert_eq!(liquidity.min_liquidity_msat(), 0);
2918		assert_eq!(liquidity.max_liquidity_msat(), 100);
2919
2920		// Reset from target to source.
2921		scorer.channel_liquidities.get_mut(&42).unwrap()
2922			.as_directed_mut(&target, &source, 1_000)
2923			.set_min_liquidity_msat(400, Duration::ZERO);
2924
2925		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2926			.as_directed(&source, &target, 1_000);
2927		assert_eq!(liquidity.min_liquidity_msat(), 0);
2928		assert_eq!(liquidity.max_liquidity_msat(), 600);
2929
2930		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2931			.as_directed(&target, &source, 1_000);
2932		assert_eq!(liquidity.min_liquidity_msat(), 400);
2933		assert_eq!(liquidity.max_liquidity_msat(), 1_000);
2934	}
2935
2936	#[test]
2937	#[rustfmt::skip]
2938	fn resets_liquidity_lower_bound_when_crossed_by_upper_bound() {
2939		let logger = TestLogger::new();
2940		let last_updated = Duration::ZERO;
2941		let offset_history_last_updated = Duration::ZERO;
2942		let last_datapoint_time = Duration::ZERO;
2943		let network_graph = network_graph(&logger);
2944		let decay_params = ProbabilisticScoringDecayParameters::default();
2945		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
2946			.with_channel(42,
2947				ChannelLiquidity {
2948					min_liquidity_offset_msat: 200, max_liquidity_offset_msat: 400,
2949					last_updated, offset_history_last_updated, last_datapoint_time,
2950					liquidity_history: HistoricalLiquidityTracker::new(),
2951				});
2952		let source = source_node_id();
2953		let target = target_node_id();
2954		assert!(source > target);
2955
2956		// Check initial bounds.
2957		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2958			.as_directed(&source, &target, 1_000);
2959		assert_eq!(liquidity.min_liquidity_msat(), 400);
2960		assert_eq!(liquidity.max_liquidity_msat(), 800);
2961
2962		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2963			.as_directed(&target, &source, 1_000);
2964		assert_eq!(liquidity.min_liquidity_msat(), 200);
2965		assert_eq!(liquidity.max_liquidity_msat(), 600);
2966
2967		// Reset from source to target.
2968		scorer.channel_liquidities.get_mut(&42).unwrap()
2969			.as_directed_mut(&source, &target, 1_000)
2970			.set_max_liquidity_msat(300, Duration::ZERO);
2971
2972		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2973			.as_directed(&source, &target, 1_000);
2974		assert_eq!(liquidity.min_liquidity_msat(), 0);
2975		assert_eq!(liquidity.max_liquidity_msat(), 300);
2976
2977		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2978			.as_directed(&target, &source, 1_000);
2979		assert_eq!(liquidity.min_liquidity_msat(), 700);
2980		assert_eq!(liquidity.max_liquidity_msat(), 1_000);
2981
2982		// Reset from target to source.
2983		scorer.channel_liquidities.get_mut(&42).unwrap()
2984			.as_directed_mut(&target, &source, 1_000)
2985			.set_max_liquidity_msat(600, Duration::ZERO);
2986
2987		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2988			.as_directed(&source, &target, 1_000);
2989		assert_eq!(liquidity.min_liquidity_msat(), 400);
2990		assert_eq!(liquidity.max_liquidity_msat(), 1_000);
2991
2992		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
2993			.as_directed(&target, &source, 1_000);
2994		assert_eq!(liquidity.min_liquidity_msat(), 0);
2995		assert_eq!(liquidity.max_liquidity_msat(), 600);
2996	}
2997
2998	#[test]
2999	#[rustfmt::skip]
3000	fn increased_penalty_nearing_liquidity_upper_bound() {
3001		let logger = TestLogger::new();
3002		let network_graph = network_graph(&logger);
3003		let params = ProbabilisticScoringFeeParameters {
3004			liquidity_penalty_multiplier_msat: 1_000,
3005			..ProbabilisticScoringFeeParameters::zero_penalty()
3006		};
3007		let decay_params = ProbabilisticScoringDecayParameters::default();
3008		let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
3009		let source = source_node_id();
3010
3011		let usage = ChannelUsage {
3012			amount_msat: 1_024,
3013			inflight_htlc_msat: 0,
3014			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
3015		};
3016		let network_graph = network_graph.read_only();
3017		let channel = network_graph.channel(42).unwrap();
3018		let (info, _) = channel.as_directed_from(&source).unwrap();
3019		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3020			info,
3021			short_channel_id: 42,
3022		});
3023		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3024		let usage = ChannelUsage { amount_msat: 10_240, ..usage };
3025		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3026		let usage = ChannelUsage { amount_msat: 102_400, ..usage };
3027		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 47);
3028		let usage = ChannelUsage { amount_msat: 1_023_999, ..usage };
3029		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2_000);
3030
3031		let usage = ChannelUsage {
3032			amount_msat: 128,
3033			inflight_htlc_msat: 0,
3034			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
3035		};
3036		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 58);
3037		let usage = ChannelUsage { amount_msat: 256, ..usage };
3038		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 125);
3039		let usage = ChannelUsage { amount_msat: 374, ..usage };
3040		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 198);
3041		let usage = ChannelUsage { amount_msat: 512, ..usage };
3042		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
3043		let usage = ChannelUsage { amount_msat: 640, ..usage };
3044		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 425);
3045		let usage = ChannelUsage { amount_msat: 768, ..usage };
3046		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 602);
3047		let usage = ChannelUsage { amount_msat: 896, ..usage };
3048		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 902);
3049	}
3050
3051	#[test]
3052	#[rustfmt::skip]
3053	fn constant_penalty_outside_liquidity_bounds() {
3054		let logger = TestLogger::new();
3055		let last_updated = Duration::ZERO;
3056		let offset_history_last_updated = Duration::ZERO;
3057		let last_datapoint_time = Duration::ZERO;
3058		let network_graph = network_graph(&logger);
3059		let params = ProbabilisticScoringFeeParameters {
3060			liquidity_penalty_multiplier_msat: 1_000,
3061			considered_impossible_penalty_msat: u64::max_value(),
3062			..ProbabilisticScoringFeeParameters::zero_penalty()
3063		};
3064		let decay_params = ProbabilisticScoringDecayParameters {
3065			..ProbabilisticScoringDecayParameters::zero_penalty()
3066		};
3067		let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
3068			.with_channel(42,
3069				ChannelLiquidity {
3070					min_liquidity_offset_msat: 40, max_liquidity_offset_msat: 40,
3071					last_updated, offset_history_last_updated, last_datapoint_time,
3072					liquidity_history: HistoricalLiquidityTracker::new(),
3073				});
3074		let source = source_node_id();
3075
3076		let usage = ChannelUsage {
3077			amount_msat: 39,
3078			inflight_htlc_msat: 0,
3079			effective_capacity: EffectiveCapacity::Total { capacity_msat: 100, htlc_maximum_msat: 1_000 },
3080		};
3081		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3082		let (info, _) = channel.as_directed_from(&source).unwrap();
3083		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3084			info,
3085			short_channel_id: 42,
3086		});
3087		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3088		let usage = ChannelUsage { amount_msat: 50, ..usage };
3089		assert_ne!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3090		assert_ne!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3091		let usage = ChannelUsage { amount_msat: 61, ..usage };
3092		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3093	}
3094
3095	#[test]
3096	#[rustfmt::skip]
3097	fn does_not_further_penalize_own_channel() {
3098		let logger = TestLogger::new();
3099		let network_graph = network_graph(&logger);
3100		let params = ProbabilisticScoringFeeParameters {
3101			liquidity_penalty_multiplier_msat: 1_000,
3102			..ProbabilisticScoringFeeParameters::zero_penalty()
3103		};
3104		let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3105		let source = source_node_id();
3106		let usage = ChannelUsage {
3107			amount_msat: 500,
3108			inflight_htlc_msat: 0,
3109			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
3110		};
3111		let failed_path = payment_path_for_amount(500);
3112		let successful_path = payment_path_for_amount(200);
3113		let channel = &network_graph.read_only().channel(42).unwrap().to_owned();
3114		let (info, _) = channel.as_directed_from(&source).unwrap();
3115		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3116			info,
3117			short_channel_id: 41,
3118		});
3119
3120		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
3121
3122		scorer.payment_path_failed(&failed_path, 41, Duration::ZERO);
3123		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
3124
3125		scorer.payment_path_successful(&successful_path, Duration::ZERO);
3126		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
3127	}
3128
3129	#[test]
3130	#[rustfmt::skip]
3131	fn sets_liquidity_lower_bound_on_downstream_failure() {
3132		let logger = TestLogger::new();
3133		let network_graph = network_graph(&logger);
3134		let params = ProbabilisticScoringFeeParameters {
3135			liquidity_penalty_multiplier_msat: 1_000,
3136			..ProbabilisticScoringFeeParameters::zero_penalty()
3137		};
3138		let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3139		let source = source_node_id();
3140		let path = payment_path_for_amount(500);
3141
3142		let usage = ChannelUsage {
3143			amount_msat: 250,
3144			inflight_htlc_msat: 0,
3145			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
3146		};
3147		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3148		let (info, _) = channel.as_directed_from(&source).unwrap();
3149		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3150			info,
3151			short_channel_id: 42,
3152		});
3153		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
3154		let usage = ChannelUsage { amount_msat: 500, ..usage };
3155		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
3156		let usage = ChannelUsage { amount_msat: 750, ..usage };
3157		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 602);
3158
3159		scorer.payment_path_failed(&path, 43, Duration::ZERO);
3160
3161		let usage = ChannelUsage { amount_msat: 250, ..usage };
3162		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3163		let usage = ChannelUsage { amount_msat: 500, ..usage };
3164		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3165		let usage = ChannelUsage { amount_msat: 750, ..usage };
3166		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
3167	}
3168
3169	#[test]
3170	#[rustfmt::skip]
3171	fn sets_liquidity_upper_bound_on_failure() {
3172		let logger = TestLogger::new();
3173		let network_graph = network_graph(&logger);
3174		let params = ProbabilisticScoringFeeParameters {
3175			liquidity_penalty_multiplier_msat: 1_000,
3176			considered_impossible_penalty_msat: u64::max_value(),
3177			..ProbabilisticScoringFeeParameters::zero_penalty()
3178		};
3179		let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3180		let source = source_node_id();
3181		let path = payment_path_for_amount(500);
3182
3183		let usage = ChannelUsage {
3184			amount_msat: 250,
3185			inflight_htlc_msat: 0,
3186			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
3187		};
3188		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3189		let (info, _) = channel.as_directed_from(&source).unwrap();
3190		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3191			info,
3192			short_channel_id: 42,
3193		});
3194		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
3195		let usage = ChannelUsage { amount_msat: 500, ..usage };
3196		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
3197		let usage = ChannelUsage { amount_msat: 750, ..usage };
3198		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 602);
3199
3200		scorer.payment_path_failed(&path, 42, Duration::ZERO);
3201
3202		let usage = ChannelUsage { amount_msat: 250, ..usage };
3203		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
3204		let usage = ChannelUsage { amount_msat: 500, ..usage };
3205		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3206		let usage = ChannelUsage { amount_msat: 750, ..usage };
3207		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3208	}
3209
3210	#[test]
3211	#[rustfmt::skip]
3212	fn ignores_channels_after_removed_failed_channel() {
3213		// Previously, if we'd tried to send over a channel which was removed from the network
3214		// graph before we call `payment_path_failed` (which is the default if the we get a "no
3215		// such channel" error in the `InvoicePayer`), we would call `failed_downstream` on all
3216		// channels in the route, even ones which they payment never reached. This tests to ensure
3217		// we do not score such channels.
3218		let secp_ctx = Secp256k1::new();
3219		let logger = TestLogger::new();
3220		let mut network_graph = NetworkGraph::new(Network::Testnet, &logger);
3221		let secret_a = SecretKey::from_slice(&[42; 32]).unwrap();
3222		let secret_b = SecretKey::from_slice(&[43; 32]).unwrap();
3223		let secret_c = SecretKey::from_slice(&[44; 32]).unwrap();
3224		let secret_d = SecretKey::from_slice(&[45; 32]).unwrap();
3225		add_channel(&mut network_graph, 42, secret_a, secret_b);
3226		// Don't add the channel from B -> C.
3227		add_channel(&mut network_graph, 44, secret_c, secret_d);
3228
3229		let pub_a = PublicKey::from_secret_key(&secp_ctx, &secret_a);
3230		let pub_b = PublicKey::from_secret_key(&secp_ctx, &secret_b);
3231		let pub_c = PublicKey::from_secret_key(&secp_ctx, &secret_c);
3232		let pub_d = PublicKey::from_secret_key(&secp_ctx, &secret_d);
3233
3234		let path = vec![
3235			path_hop(pub_b, 42, 1),
3236			path_hop(pub_c, 43, 2),
3237			path_hop(pub_d, 44, 100),
3238		];
3239
3240		let node_a = NodeId::from_pubkey(&pub_a);
3241		let node_b = NodeId::from_pubkey(&pub_b);
3242		let node_c = NodeId::from_pubkey(&pub_c);
3243
3244		let params = ProbabilisticScoringFeeParameters {
3245			liquidity_penalty_multiplier_msat: 1_000,
3246			..ProbabilisticScoringFeeParameters::zero_penalty()
3247		};
3248		let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3249
3250		let usage = ChannelUsage {
3251			amount_msat: 250,
3252			inflight_htlc_msat: 0,
3253			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
3254		};
3255		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3256		let (info, _) = channel.as_directed_from(&node_a).unwrap();
3257		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3258			info,
3259			short_channel_id: 42,
3260		});
3261		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
3262		// Note that a default liquidity bound is used for B -> C as no channel exists
3263		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3264		let (info, _) = channel.as_directed_from(&node_b).unwrap();
3265		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3266			info,
3267			short_channel_id: 43,
3268		});
3269		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
3270		let channel = network_graph.read_only().channel(44).unwrap().to_owned();
3271		let (info, _) = channel.as_directed_from(&node_c).unwrap();
3272		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3273			info,
3274			short_channel_id: 44,
3275		});
3276		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
3277
3278		scorer.payment_path_failed(&Path { hops: path, blinded_tail: None }, 43, Duration::ZERO);
3279
3280		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3281		let (info, _) = channel.as_directed_from(&node_a).unwrap();
3282		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3283			info,
3284			short_channel_id: 42,
3285		});
3286		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 80);
3287		// Note that a default liquidity bound is used for B -> C as no channel exists
3288		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3289		let (info, _) = channel.as_directed_from(&node_b).unwrap();
3290		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3291			info,
3292			short_channel_id: 43,
3293		});
3294		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
3295		let channel = network_graph.read_only().channel(44).unwrap().to_owned();
3296		let (info, _) = channel.as_directed_from(&node_c).unwrap();
3297		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3298			info,
3299			short_channel_id: 44,
3300		});
3301		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
3302	}
3303
3304	#[test]
3305	#[rustfmt::skip]
3306	fn reduces_liquidity_upper_bound_along_path_on_success() {
3307		let logger = TestLogger::new();
3308		let network_graph = network_graph(&logger);
3309		let params = ProbabilisticScoringFeeParameters {
3310			liquidity_penalty_multiplier_msat: 1_000,
3311			..ProbabilisticScoringFeeParameters::zero_penalty()
3312		};
3313		let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3314		let source = source_node_id();
3315		let usage = ChannelUsage {
3316			amount_msat: 250,
3317			inflight_htlc_msat: 0,
3318			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
3319		};
3320		let network_graph = network_graph.read_only().channels().clone();
3321		let channel_42 = network_graph.get(&42).unwrap();
3322		let channel_43 = network_graph.get(&43).unwrap();
3323		let (info, _) = channel_42.as_directed_from(&source).unwrap();
3324		let candidate_41 = CandidateRouteHop::PublicHop(PublicHopCandidate {
3325			info,
3326			short_channel_id: 41,
3327		});
3328		let (info, target) = channel_42.as_directed_from(&source).unwrap();
3329		let candidate_42 = CandidateRouteHop::PublicHop(PublicHopCandidate {
3330			info,
3331			short_channel_id: 42,
3332		});
3333		let (info, _) = channel_43.as_directed_from(&target).unwrap();
3334		let candidate_43 = CandidateRouteHop::PublicHop(PublicHopCandidate {
3335			info,
3336			short_channel_id: 43,
3337		});
3338		assert_eq!(scorer.channel_penalty_msat(&candidate_41, usage, &params), 128);
3339		assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, &params), 128);
3340		assert_eq!(scorer.channel_penalty_msat(&candidate_43, usage, &params), 128);
3341
3342		scorer.payment_path_successful(&payment_path_for_amount(500), Duration::ZERO);
3343
3344		assert_eq!(scorer.channel_penalty_msat(&candidate_41, usage, &params), 128);
3345		assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, &params), 300);
3346		assert_eq!(scorer.channel_penalty_msat(&candidate_43, usage, &params), 300);
3347	}
3348
3349	#[test]
3350	#[rustfmt::skip]
3351	fn decays_liquidity_bounds_over_time() {
3352		let logger = TestLogger::new();
3353		let network_graph = network_graph(&logger);
3354		let params = ProbabilisticScoringFeeParameters {
3355			liquidity_penalty_multiplier_msat: 1_000,
3356			considered_impossible_penalty_msat: u64::max_value(),
3357			..ProbabilisticScoringFeeParameters::zero_penalty()
3358		};
3359		let decay_params = ProbabilisticScoringDecayParameters {
3360			liquidity_offset_half_life: Duration::from_secs(10),
3361			..ProbabilisticScoringDecayParameters::zero_penalty()
3362		};
3363		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
3364		let source = source_node_id();
3365
3366		let usage = ChannelUsage {
3367			amount_msat: 0,
3368			inflight_htlc_msat: 0,
3369			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
3370		};
3371		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3372		let (info, _) = channel.as_directed_from(&source).unwrap();
3373		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3374			info,
3375			short_channel_id: 42,
3376		});
3377		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3378		let usage = ChannelUsage { amount_msat: 1_023, ..usage };
3379		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2_000);
3380
3381		scorer.payment_path_failed(&payment_path_for_amount(768), 42, Duration::ZERO);
3382		scorer.payment_path_failed(&payment_path_for_amount(128), 43, Duration::ZERO);
3383
3384		// Initial penalties
3385		let usage = ChannelUsage { amount_msat: 128, ..usage };
3386		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3387		let usage = ChannelUsage { amount_msat: 256, ..usage };
3388		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 93);
3389		let usage = ChannelUsage { amount_msat: 768, ..usage };
3390		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 1_479);
3391		let usage = ChannelUsage { amount_msat: 896, ..usage };
3392		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3393
3394		// Half decay (i.e., three-quarter life)
3395		scorer.time_passed(Duration::from_secs(5));
3396		let usage = ChannelUsage { amount_msat: 128, ..usage };
3397		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 22);
3398		let usage = ChannelUsage { amount_msat: 256, ..usage };
3399		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 106);
3400		let usage = ChannelUsage { amount_msat: 768, ..usage };
3401		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 921);
3402		let usage = ChannelUsage { amount_msat: 896, ..usage };
3403		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3404
3405		// One decay (i.e., half life)
3406		scorer.time_passed(Duration::from_secs(10));
3407		let usage = ChannelUsage { amount_msat: 64, ..usage };
3408		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3409		let usage = ChannelUsage { amount_msat: 128, ..usage };
3410		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 34);
3411		let usage = ChannelUsage { amount_msat: 896, ..usage };
3412		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 1_970);
3413		let usage = ChannelUsage { amount_msat: 960, ..usage };
3414		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3415
3416		// Fully decay liquidity lower bound.
3417		scorer.time_passed(Duration::from_secs(10 * 8));
3418		let usage = ChannelUsage { amount_msat: 0, ..usage };
3419		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3420		let usage = ChannelUsage { amount_msat: 1, ..usage };
3421		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3422		let usage = ChannelUsage { amount_msat: 1_023, ..usage };
3423		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2_000);
3424		let usage = ChannelUsage { amount_msat: 1_024, ..usage };
3425		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3426
3427		// Fully decay liquidity upper bound.
3428		scorer.time_passed(Duration::from_secs(10 * 9));
3429		let usage = ChannelUsage { amount_msat: 0, ..usage };
3430		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3431		let usage = ChannelUsage { amount_msat: 1_024, ..usage };
3432		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3433
3434		scorer.time_passed(Duration::from_secs(10 * 10));
3435		let usage = ChannelUsage { amount_msat: 0, ..usage };
3436		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
3437		let usage = ChannelUsage { amount_msat: 1_024, ..usage };
3438		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3439	}
3440
3441	#[test]
3442	#[rustfmt::skip]
3443	fn restricts_liquidity_bounds_after_decay() {
3444		let logger = TestLogger::new();
3445		let network_graph = network_graph(&logger);
3446		let params = ProbabilisticScoringFeeParameters {
3447			liquidity_penalty_multiplier_msat: 1_000,
3448			..ProbabilisticScoringFeeParameters::zero_penalty()
3449		};
3450		let decay_params = ProbabilisticScoringDecayParameters {
3451			liquidity_offset_half_life: Duration::from_secs(10),
3452			..ProbabilisticScoringDecayParameters::default()
3453		};
3454		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
3455		let source = source_node_id();
3456		let usage = ChannelUsage {
3457			amount_msat: 512,
3458			inflight_htlc_msat: 0,
3459			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
3460		};
3461		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3462		let (info, _) = channel.as_directed_from(&source).unwrap();
3463		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3464			info,
3465			short_channel_id: 42,
3466		});
3467
3468		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
3469
3470		// More knowledge gives higher confidence (256, 768), meaning a lower penalty.
3471		scorer.payment_path_failed(&payment_path_for_amount(768), 42, Duration::ZERO);
3472		scorer.payment_path_failed(&payment_path_for_amount(256), 43, Duration::ZERO);
3473		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 281);
3474
3475		// Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
3476		scorer.time_passed(Duration::from_secs(10));
3477		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 291);
3478
3479		// Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
3480		// is closer to the upper bound, meaning a higher penalty.
3481		scorer.payment_path_successful(&payment_path_for_amount(64), Duration::from_secs(10));
3482		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 331);
3483
3484		// Increasing the lower bound gives more confidence (256, 832) that the payment amount (512)
3485		// is closer to the lower bound, meaning a lower penalty.
3486		scorer.payment_path_failed(&payment_path_for_amount(256), 43, Duration::from_secs(10));
3487		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 245);
3488
3489		// Further decaying affects the lower bound more than the upper bound (128, 928).
3490		scorer.time_passed(Duration::from_secs(20));
3491		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 280);
3492	}
3493
3494	#[test]
3495	#[rustfmt::skip]
3496	fn restores_persisted_liquidity_bounds() {
3497		let logger = TestLogger::new();
3498		let network_graph = network_graph(&logger);
3499		let params = ProbabilisticScoringFeeParameters {
3500			liquidity_penalty_multiplier_msat: 1_000,
3501			considered_impossible_penalty_msat: u64::max_value(),
3502			..ProbabilisticScoringFeeParameters::zero_penalty()
3503		};
3504		let decay_params = ProbabilisticScoringDecayParameters {
3505			liquidity_offset_half_life: Duration::from_secs(10),
3506			..ProbabilisticScoringDecayParameters::default()
3507		};
3508		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
3509		let source = source_node_id();
3510		let usage = ChannelUsage {
3511			amount_msat: 500,
3512			inflight_htlc_msat: 0,
3513			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
3514		};
3515
3516		scorer.payment_path_failed(&payment_path_for_amount(500), 42, Duration::ZERO);
3517		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3518		let (info, _) = channel.as_directed_from(&source).unwrap();
3519		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3520			info,
3521			short_channel_id: 42,
3522		});
3523		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3524
3525		scorer.time_passed(Duration::from_secs(10));
3526		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 473);
3527
3528		scorer.payment_path_failed(&payment_path_for_amount(250), 43, Duration::from_secs(10));
3529		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
3530
3531		let mut serialized_scorer = Vec::new();
3532		scorer.write(&mut serialized_scorer).unwrap();
3533
3534		let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
3535		let deserialized_scorer =
3536			<ProbabilisticScorer<_, _>>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
3537		assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, &params), 300);
3538	}
3539
3540	#[rustfmt::skip]
3541	fn do_decays_persisted_liquidity_bounds(decay_before_reload: bool) {
3542		let logger = TestLogger::new();
3543		let network_graph = network_graph(&logger);
3544		let params = ProbabilisticScoringFeeParameters {
3545			liquidity_penalty_multiplier_msat: 1_000,
3546			considered_impossible_penalty_msat: u64::max_value(),
3547			..ProbabilisticScoringFeeParameters::zero_penalty()
3548		};
3549		let decay_params = ProbabilisticScoringDecayParameters {
3550			liquidity_offset_half_life: Duration::from_secs(10),
3551			..ProbabilisticScoringDecayParameters::zero_penalty()
3552		};
3553		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
3554		let source = source_node_id();
3555		let usage = ChannelUsage {
3556			amount_msat: 500,
3557			inflight_htlc_msat: 0,
3558			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
3559		};
3560
3561		scorer.payment_path_failed(&payment_path_for_amount(500), 42, Duration::ZERO);
3562		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3563		let (info, _) = channel.as_directed_from(&source).unwrap();
3564		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3565			info,
3566			short_channel_id: 42,
3567		});
3568		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3569
3570		if decay_before_reload {
3571			scorer.time_passed(Duration::from_secs(10));
3572		}
3573
3574		let mut serialized_scorer = Vec::new();
3575		scorer.write(&mut serialized_scorer).unwrap();
3576
3577		let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
3578		let mut deserialized_scorer =
3579			<ProbabilisticScorer<_, _>>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
3580		if !decay_before_reload {
3581			scorer.time_passed(Duration::from_secs(10));
3582			deserialized_scorer.time_passed(Duration::from_secs(10));
3583		}
3584		assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, &params), 473);
3585
3586		scorer.payment_path_failed(&payment_path_for_amount(250), 43, Duration::from_secs(10));
3587		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
3588
3589		deserialized_scorer.time_passed(Duration::from_secs(20));
3590		assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, &params), 370);
3591	}
3592
3593	#[test]
3594	fn decays_persisted_liquidity_bounds() {
3595		do_decays_persisted_liquidity_bounds(false);
3596		do_decays_persisted_liquidity_bounds(true);
3597	}
3598
3599	#[test]
3600	#[rustfmt::skip]
3601	fn scores_realistic_payments() {
3602		// Shows the scores of "realistic" sends of 100k sats over channels of 1-10m sats (with a
3603		// 50k sat reserve).
3604		let logger = TestLogger::new();
3605		let network_graph = network_graph(&logger);
3606		let params = ProbabilisticScoringFeeParameters::default();
3607		let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3608		let source = source_node_id();
3609
3610		let usage = ChannelUsage {
3611			amount_msat: 100_000_000,
3612			inflight_htlc_msat: 0,
3613			effective_capacity: EffectiveCapacity::Total { capacity_msat: 950_000_000, htlc_maximum_msat: 1_000 },
3614		};
3615		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3616		let (info, _) = channel.as_directed_from(&source).unwrap();
3617		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3618			info,
3619			short_channel_id: 42,
3620		});
3621		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 42_252);
3622		let usage = ChannelUsage {
3623			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
3624		};
3625		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 36_005);
3626		let usage = ChannelUsage {
3627			effective_capacity: EffectiveCapacity::Total { capacity_msat: 2_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
3628		};
3629		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 32_851);
3630		let usage = ChannelUsage {
3631			effective_capacity: EffectiveCapacity::Total { capacity_msat: 3_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
3632		};
3633		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 30_832);
3634		let usage = ChannelUsage {
3635			effective_capacity: EffectiveCapacity::Total { capacity_msat: 4_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
3636		};
3637		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 29_886);
3638		let usage = ChannelUsage {
3639			effective_capacity: EffectiveCapacity::Total { capacity_msat: 5_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
3640		};
3641		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 28_939);
3642		let usage = ChannelUsage {
3643			effective_capacity: EffectiveCapacity::Total { capacity_msat: 6_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
3644		};
3645		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 28_435);
3646		let usage = ChannelUsage {
3647			effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_450_000_000, htlc_maximum_msat: 1_000 }, ..usage
3648		};
3649		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 27_993);
3650		let usage = ChannelUsage {
3651			effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
3652		};
3653		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 27_993);
3654		let usage = ChannelUsage {
3655			effective_capacity: EffectiveCapacity::Total { capacity_msat: 8_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
3656		};
3657		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 27_488);
3658		let usage = ChannelUsage {
3659			effective_capacity: EffectiveCapacity::Total { capacity_msat: 9_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
3660		};
3661		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 27_047);
3662	}
3663
3664	#[test]
3665	#[rustfmt::skip]
3666	fn adds_base_penalty_to_liquidity_penalty() {
3667		let logger = TestLogger::new();
3668		let network_graph = network_graph(&logger);
3669		let source = source_node_id();
3670		let usage = ChannelUsage {
3671			amount_msat: 128,
3672			inflight_htlc_msat: 0,
3673			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
3674		};
3675
3676		let params = ProbabilisticScoringFeeParameters {
3677			liquidity_penalty_multiplier_msat: 1_000,
3678			..ProbabilisticScoringFeeParameters::zero_penalty()
3679		};
3680		let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3681		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3682		let (info, _) = channel.as_directed_from(&source).unwrap();
3683		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3684			info,
3685			short_channel_id: 42,
3686		});
3687		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 58);
3688
3689		let params = ProbabilisticScoringFeeParameters {
3690			base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
3691			anti_probing_penalty_msat: 0, ..ProbabilisticScoringFeeParameters::zero_penalty()
3692		};
3693		let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3694		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 558);
3695
3696		let params = ProbabilisticScoringFeeParameters {
3697			base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
3698			base_penalty_amount_multiplier_msat: (1 << 30),
3699			anti_probing_penalty_msat: 0, ..ProbabilisticScoringFeeParameters::zero_penalty()
3700		};
3701
3702		let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3703		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 558 + 128);
3704	}
3705
3706	#[test]
3707	#[rustfmt::skip]
3708	fn adds_amount_penalty_to_liquidity_penalty() {
3709		let logger = TestLogger::new();
3710		let network_graph = network_graph(&logger);
3711		let source = source_node_id();
3712		let usage = ChannelUsage {
3713			amount_msat: 512_000,
3714			inflight_htlc_msat: 0,
3715			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
3716		};
3717
3718		let params = ProbabilisticScoringFeeParameters {
3719			liquidity_penalty_multiplier_msat: 1_000,
3720			liquidity_penalty_amount_multiplier_msat: 0,
3721			..ProbabilisticScoringFeeParameters::zero_penalty()
3722		};
3723		let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3724		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3725		let (info, _) = channel.as_directed_from(&source).unwrap();
3726		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3727			info,
3728			short_channel_id: 42,
3729		});
3730		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
3731
3732		let params = ProbabilisticScoringFeeParameters {
3733			liquidity_penalty_multiplier_msat: 1_000,
3734			liquidity_penalty_amount_multiplier_msat: 256,
3735			..ProbabilisticScoringFeeParameters::zero_penalty()
3736		};
3737		let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3738		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 337);
3739	}
3740
3741	#[test]
3742	#[rustfmt::skip]
3743	fn calculates_log10_without_overflowing_u64_max_value() {
3744		let logger = TestLogger::new();
3745		let network_graph = network_graph(&logger);
3746		let source = source_node_id();
3747		let usage = ChannelUsage {
3748			amount_msat: u64::max_value(),
3749			inflight_htlc_msat: 0,
3750			effective_capacity: EffectiveCapacity::Infinite,
3751		};
3752		let params = ProbabilisticScoringFeeParameters {
3753			liquidity_penalty_multiplier_msat: 40_000,
3754			..ProbabilisticScoringFeeParameters::zero_penalty()
3755		};
3756		let decay_params = ProbabilisticScoringDecayParameters::zero_penalty();
3757		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
3758		let (info, _) = channel.as_directed_from(&source).unwrap();
3759		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3760			info,
3761			short_channel_id: 42,
3762		});
3763		let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
3764		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 80_000);
3765	}
3766
3767	#[test]
3768	#[rustfmt::skip]
3769	fn accounts_for_inflight_htlc_usage() {
3770		let logger = TestLogger::new();
3771		let network_graph = network_graph(&logger);
3772		let params = ProbabilisticScoringFeeParameters {
3773			considered_impossible_penalty_msat: u64::max_value(),
3774			..ProbabilisticScoringFeeParameters::zero_penalty()
3775		};
3776		let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3777		let source = source_node_id();
3778
3779		let usage = ChannelUsage {
3780			amount_msat: 750,
3781			inflight_htlc_msat: 0,
3782			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
3783		};
3784		let network_graph = network_graph.read_only();
3785		let channel = network_graph.channel(42).unwrap();
3786		let (info, _) = channel.as_directed_from(&source).unwrap();
3787		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3788			info,
3789			short_channel_id: 42,
3790		});
3791		assert_ne!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3792
3793		let usage = ChannelUsage { inflight_htlc_msat: 251, ..usage };
3794		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3795	}
3796
3797	#[test]
3798	#[rustfmt::skip]
3799	fn removes_uncertainity_when_exact_liquidity_known() {
3800		let logger = TestLogger::new();
3801		let network_graph = network_graph(&logger);
3802		let params = ProbabilisticScoringFeeParameters::default();
3803		let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
3804		let source = source_node_id();
3805
3806		let base_penalty_msat = params.base_penalty_msat;
3807		let usage = ChannelUsage {
3808			amount_msat: 750,
3809			inflight_htlc_msat: 0,
3810			effective_capacity: EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 },
3811		};
3812		let network_graph = network_graph.read_only();
3813		let channel = network_graph.channel(42).unwrap();
3814		let (info, _) = channel.as_directed_from(&source).unwrap();
3815		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3816			info,
3817			short_channel_id: 42,
3818		});
3819		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), base_penalty_msat);
3820
3821		let usage = ChannelUsage { amount_msat: 1_000, ..usage };
3822		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), base_penalty_msat);
3823
3824		let usage = ChannelUsage { amount_msat: 1_001, ..usage };
3825		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
3826	}
3827
3828	#[test]
3829	#[rustfmt::skip]
3830	fn remembers_historical_failures() {
3831		let logger = TestLogger::new();
3832		let network_graph = network_graph(&logger);
3833		let params = ProbabilisticScoringFeeParameters {
3834			historical_liquidity_penalty_multiplier_msat: 1024,
3835			historical_liquidity_penalty_amount_multiplier_msat: 1024,
3836			..ProbabilisticScoringFeeParameters::zero_penalty()
3837		};
3838		let decay_params = ProbabilisticScoringDecayParameters {
3839			liquidity_offset_half_life: Duration::from_secs(60 * 60),
3840			historical_no_updates_half_life: Duration::from_secs(10),
3841		};
3842		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
3843		let source = source_node_id();
3844		let target = target_node_id();
3845
3846		let usage = ChannelUsage {
3847			amount_msat: 100,
3848			inflight_htlc_msat: 0,
3849			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
3850		};
3851		let usage_1 = ChannelUsage {
3852			amount_msat: 1,
3853			inflight_htlc_msat: 0,
3854			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
3855		};
3856
3857		{
3858			let network_graph = network_graph.read_only();
3859			let channel = network_graph.channel(42).unwrap();
3860			let (info, _) = channel.as_directed_from(&source).unwrap();
3861			let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3862				info,
3863				short_channel_id: 42,
3864			});
3865
3866			// With no historical data the normal liquidity penalty calculation is used.
3867			assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 135);
3868		}
3869		assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
3870		None);
3871		assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, &params, false),
3872		None);
3873
3874		scorer.payment_path_failed(&payment_path_for_amount(1), 42, Duration::ZERO);
3875		{
3876			let network_graph = network_graph.read_only();
3877			let channel = network_graph.channel(42).unwrap();
3878			let (info, _) = channel.as_directed_from(&source).unwrap();
3879			let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3880				info,
3881				short_channel_id: 42,
3882			});
3883
3884			assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2048);
3885			assert_eq!(scorer.channel_penalty_msat(&candidate, usage_1, &params), 220);
3886		}
3887		// The "it failed" increment is 32, where the probability should lie several buckets into
3888		// the first octile.
3889		assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
3890			Some(([32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
3891				[0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])));
3892		assert!(scorer.historical_estimated_payment_success_probability(42, &target, 1, &params, false)
3893			.unwrap() > 0.35);
3894		assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 500, &params, false),
3895			Some(0.0));
3896
3897		// Even after we tell the scorer we definitely have enough available liquidity, it will
3898		// still remember that there was some failure in the past, and assign a non-0 penalty.
3899		scorer.payment_path_failed(&payment_path_for_amount(1000), 43, Duration::ZERO);
3900		{
3901			let network_graph = network_graph.read_only();
3902			let channel = network_graph.channel(42).unwrap();
3903			let (info, _) = channel.as_directed_from(&source).unwrap();
3904			let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3905				info,
3906				short_channel_id: 42,
3907			});
3908
3909			assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 83);
3910		}
3911		// The first points should be decayed just slightly and the last bucket has a new point.
3912		assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
3913			Some(([31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0],
3914				[0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32])));
3915
3916		// The exact success probability is a bit complicated and involves integer rounding, so we
3917		// simply check bounds here.
3918		let five_hundred_prob =
3919			scorer.historical_estimated_payment_success_probability(42, &target, 500, &params, false).unwrap();
3920		assert!(five_hundred_prob > 0.61, "{}", five_hundred_prob);
3921		assert!(five_hundred_prob < 0.62, "{}", five_hundred_prob);
3922		let one_prob =
3923			scorer.historical_estimated_payment_success_probability(42, &target, 1, &params, false).unwrap();
3924		assert!(one_prob < 0.89, "{}", one_prob);
3925		assert!(one_prob > 0.88, "{}", one_prob);
3926
3927		// Advance the time forward 16 half-lives (which the docs claim will ensure all data is
3928		// gone), and check that we're back to where we started.
3929		scorer.time_passed(Duration::from_secs(10 * 16));
3930		{
3931			let network_graph = network_graph.read_only();
3932			let channel = network_graph.channel(42).unwrap();
3933			let (info, _) = channel.as_directed_from(&source).unwrap();
3934			let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3935				info,
3936				short_channel_id: 42,
3937			});
3938
3939			assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 135);
3940		}
3941		// Once fully decayed we still have data, but its all-0s. In the future we may remove the
3942		// data entirely instead.
3943		assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
3944			Some(([0; 32], [0; 32])));
3945		assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 1, &params, false), None);
3946
3947		let usage = ChannelUsage {
3948			amount_msat: 100,
3949			inflight_htlc_msat: 1024,
3950			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
3951		};
3952		scorer.payment_path_failed(&payment_path_for_amount(1), 42, Duration::from_secs(10 * 16));
3953		{
3954			let network_graph = network_graph.read_only();
3955			let channel = network_graph.channel(42).unwrap();
3956			let (info, _) = channel.as_directed_from(&source).unwrap();
3957			let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
3958				info,
3959				short_channel_id: 42,
3960			});
3961
3962			assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2048);
3963
3964			let usage = ChannelUsage {
3965				amount_msat: 1,
3966				inflight_htlc_msat: 0,
3967				effective_capacity: EffectiveCapacity::AdvertisedMaxHTLC { amount_msat: 0 },
3968			};
3969			assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2048);
3970		}
3971
3972		// Advance to decay all liquidity offsets to zero.
3973		scorer.time_passed(Duration::from_secs(10 * (16 + 60 * 60)));
3974
3975		// Once even the bounds have decayed information about the channel should be removed
3976		// entirely.
3977		assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
3978			None);
3979
3980		// Use a path in the opposite direction, which have zero for htlc_maximum_msat. This will
3981		// ensure that the effective capacity is zero to test division-by-zero edge cases.
3982		let path = vec![
3983			path_hop(target_pubkey(), 43, 2),
3984			path_hop(source_pubkey(), 42, 1),
3985			path_hop(sender_pubkey(), 41, 0),
3986		];
3987		scorer.payment_path_failed(&Path { hops: path, blinded_tail: None }, 42, Duration::from_secs(10 * (16 + 60 * 60)));
3988	}
3989
3990	#[test]
3991	#[rustfmt::skip]
3992	fn adds_anti_probing_penalty() {
3993		let logger = TestLogger::new();
3994		let network_graph = network_graph(&logger);
3995		let source = source_node_id();
3996		let params = ProbabilisticScoringFeeParameters {
3997			anti_probing_penalty_msat: 500,
3998			..ProbabilisticScoringFeeParameters::zero_penalty()
3999		};
4000		let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
4001
4002		// Check we receive no penalty for a low htlc_maximum_msat.
4003		let usage = ChannelUsage {
4004			amount_msat: 512_000,
4005			inflight_htlc_msat: 0,
4006			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
4007		};
4008		let network_graph = network_graph.read_only();
4009		let channel = network_graph.channel(42).unwrap();
4010		let (info, _) = channel.as_directed_from(&source).unwrap();
4011		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
4012			info,
4013			short_channel_id: 42,
4014		});
4015		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
4016
4017		// Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity.
4018		let usage = ChannelUsage {
4019			amount_msat: 512_000,
4020			inflight_htlc_msat: 0,
4021			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_024_000 },
4022		};
4023		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 500);
4024
4025		// Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity/2.
4026		let usage = ChannelUsage {
4027			amount_msat: 512_000,
4028			inflight_htlc_msat: 0,
4029			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 512_000 },
4030		};
4031		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 500);
4032
4033		// Check we receive no anti-probing penalty for htlc_maximum_msat == channel_capacity/2 - 1.
4034		let usage = ChannelUsage {
4035			amount_msat: 512_000,
4036			inflight_htlc_msat: 0,
4037			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 511_999 },
4038		};
4039		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
4040	}
4041
4042	#[test]
4043	#[rustfmt::skip]
4044	fn scores_with_blinded_path() {
4045		// Make sure we'll account for a blinded path's final_value_msat in scoring
4046		let logger = TestLogger::new();
4047		let network_graph = network_graph(&logger);
4048		let params = ProbabilisticScoringFeeParameters {
4049			liquidity_penalty_multiplier_msat: 1_000,
4050			..ProbabilisticScoringFeeParameters::zero_penalty()
4051		};
4052		let decay_params = ProbabilisticScoringDecayParameters::default();
4053		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
4054		let source = source_node_id();
4055		let usage = ChannelUsage {
4056			amount_msat: 512,
4057			inflight_htlc_msat: 0,
4058			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
4059		};
4060		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
4061		let (info, target) = channel.as_directed_from(&source).unwrap();
4062		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
4063			info,
4064			short_channel_id: 42,
4065		});
4066		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
4067
4068		let mut path = payment_path_for_amount(768);
4069		let recipient_hop = path.hops.pop().unwrap();
4070		path.blinded_tail = Some(BlindedTail {
4071			trampoline_hops: vec![],
4072			hops: vec![BlindedHop { blinded_node_id: test_utils::pubkey(44), encrypted_payload: Vec::new() }],
4073			blinding_point: test_utils::pubkey(42),
4074			excess_final_cltv_expiry_delta: recipient_hop.cltv_expiry_delta,
4075			final_value_msat: recipient_hop.fee_msat,
4076		});
4077
4078		// Check the liquidity before and after scoring payment failures to ensure the blinded path's
4079		// final value is taken into account.
4080		assert!(scorer.channel_liquidities.get(&42).is_none());
4081
4082		scorer.payment_path_failed(&path, 42, Duration::ZERO);
4083		path.blinded_tail.as_mut().unwrap().final_value_msat = 256;
4084		scorer.payment_path_failed(&path, 43, Duration::ZERO);
4085
4086		let liquidity = scorer.channel_liquidities.get(&42).unwrap()
4087			.as_directed(&source, &target, 1_000);
4088		assert_eq!(liquidity.min_liquidity_msat(), 256);
4089		assert_eq!(liquidity.max_liquidity_msat(), 768);
4090	}
4091
4092	#[test]
4093	#[rustfmt::skip]
4094	fn realistic_historical_failures() {
4095		// The motivation for the unequal sized buckets came largely from attempting to pay 10k
4096		// sats over a one bitcoin channel. This tests that case explicitly, ensuring that we score
4097		// properly.
4098		let logger = TestLogger::new();
4099		let mut network_graph = network_graph(&logger);
4100		let params = ProbabilisticScoringFeeParameters {
4101			historical_liquidity_penalty_multiplier_msat: 1024,
4102			historical_liquidity_penalty_amount_multiplier_msat: 1024,
4103			..ProbabilisticScoringFeeParameters::zero_penalty()
4104		};
4105		let decay_params = ProbabilisticScoringDecayParameters {
4106			liquidity_offset_half_life: Duration::from_secs(60 * 60),
4107			historical_no_updates_half_life: Duration::from_secs(10),
4108		};
4109
4110		let capacity_msat = 100_000_000_000;
4111		update_channel(&mut network_graph, 42, source_privkey(), 0, capacity_msat, 200);
4112		update_channel(&mut network_graph, 42, target_privkey(), 1, capacity_msat, 200);
4113
4114		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
4115		let source = source_node_id();
4116
4117		let mut amount_msat = 10_000_000;
4118		let usage = ChannelUsage {
4119			amount_msat,
4120			inflight_htlc_msat: 0,
4121			effective_capacity: EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: capacity_msat },
4122		};
4123		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
4124		let (info, target) = channel.as_directed_from(&source).unwrap();
4125		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
4126			info,
4127			short_channel_id: 42,
4128		});
4129		// With no historical data the normal liquidity penalty calculation is used, which results
4130		// in a success probability of ~82%.
4131		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 910);
4132		assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
4133			None);
4134		assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, &params, false),
4135			None);
4136
4137		// Fail to pay once, and then check the buckets and penalty.
4138		scorer.payment_path_failed(&payment_path_for_amount(amount_msat), 42, Duration::ZERO);
4139		// The penalty should be the maximum penalty, as the payment we're scoring is now in the
4140		// same bucket which is the only maximum datapoint.
4141		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params),
4142			2048 + 2048 * amount_msat / super::AMOUNT_PENALTY_DIVISOR);
4143		// The "it failed" increment is 32, which we should apply to the first upper-bound (between
4144		// 6k sats and 12k sats).
4145		assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
4146			Some(([32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
4147				[0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])));
4148		// The success probability estimate itself should be zero.
4149		assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params, false),
4150			Some(0.0));
4151
4152		// Now test again with the amount in the bottom bucket.
4153		amount_msat /= 2;
4154		// The new amount is entirely within the only minimum bucket with score, so the probability
4155		// we assign is 1/2.
4156		assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params, false),
4157			Some(0.5));
4158
4159		// ...but once we see a failure, we consider the payment to be substantially less likely,
4160		// even though not a probability of zero as we still look at the second max bucket which
4161		// now shows 31.
4162		scorer.payment_path_failed(&payment_path_for_amount(amount_msat), 42, Duration::ZERO);
4163		assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
4164			Some(([63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
4165				[32, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])));
4166		assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params, false),
4167			Some(0.0));
4168	}
4169
4170	#[test]
4171	#[rustfmt::skip]
4172	fn get_scores() {
4173		let logger = TestLogger::new();
4174		let network_graph = network_graph(&logger);
4175		let params = ProbabilisticScoringFeeParameters {
4176			liquidity_penalty_multiplier_msat: 1_000,
4177			..ProbabilisticScoringFeeParameters::zero_penalty()
4178		};
4179		let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
4180		let source = source_node_id();
4181		let usage = ChannelUsage {
4182			amount_msat: 500,
4183			inflight_htlc_msat: 0,
4184			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
4185		};
4186		let successful_path = payment_path_for_amount(200);
4187		let channel = &network_graph.read_only().channel(42).unwrap().to_owned();
4188		let (info, _) = channel.as_directed_from(&source).unwrap();
4189		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
4190			info,
4191			short_channel_id: 41,
4192		});
4193
4194		scorer.payment_path_successful(&successful_path, Duration::ZERO);
4195		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
4196
4197		// Get the scores and assert that both channels are present in the returned struct.
4198		let scores = scorer.scores();
4199		assert_eq!(scores.iter().count(), 2);
4200	}
4201
4202	#[test]
4203	fn combined_scorer() {
4204		let logger = TestLogger::new();
4205		let network_graph = network_graph(&logger);
4206		let params = ProbabilisticScoringFeeParameters::default();
4207		let mut scorer = ProbabilisticScorer::new(
4208			ProbabilisticScoringDecayParameters::default(),
4209			&network_graph,
4210			&logger,
4211		);
4212		scorer.payment_path_failed(&payment_path_for_amount(600), 42, Duration::ZERO);
4213
4214		let mut combined_scorer = CombinedScorer::new(scorer);
4215
4216		// Verify that the combined_scorer has the correct liquidity range after a failed 600 msat payment.
4217		let liquidity_range =
4218			combined_scorer.scorer.estimated_channel_liquidity_range(42, &target_node_id());
4219		assert_eq!(liquidity_range.unwrap(), (0, 600));
4220
4221		let source = source_node_id();
4222		let usage = ChannelUsage {
4223			amount_msat: 750,
4224			inflight_htlc_msat: 0,
4225			effective_capacity: EffectiveCapacity::Total {
4226				capacity_msat: 1_000,
4227				htlc_maximum_msat: 1_000,
4228			},
4229		};
4230
4231		let logger_rc = Rc::new(&logger);
4232
4233		let mut external_liquidity = ChannelLiquidity::new(Duration::ZERO);
4234		external_liquidity.as_directed_mut(&source_node_id(), &target_node_id(), 1_000).successful(
4235			1000,
4236			Duration::ZERO,
4237			format_args!("test channel"),
4238			logger_rc.as_ref(),
4239		);
4240
4241		let mut external_scores = ChannelLiquidities::new();
4242		external_scores.insert(42, external_liquidity);
4243
4244		{
4245			let network_graph = network_graph.read_only();
4246			let channel = network_graph.channel(42).unwrap();
4247			let (info, _) = channel.as_directed_from(&source).unwrap();
4248			let candidate =
4249				CandidateRouteHop::PublicHop(PublicHopCandidate { info, short_channel_id: 42 });
4250
4251			let penalty = combined_scorer.channel_penalty_msat(&candidate, usage, &params);
4252
4253			combined_scorer.merge(external_scores.clone(), Duration::ZERO);
4254
4255			let penalty_after_merge =
4256				combined_scorer.channel_penalty_msat(&candidate, usage, &params);
4257
4258			// Since the external source observed a successful payment, the penalty should be lower after the merge.
4259			assert!(penalty_after_merge < penalty);
4260		}
4261
4262		// Verify that after the merge with a successful payment, the liquidity range is increased.
4263		let liquidity_range =
4264			combined_scorer.scorer.estimated_channel_liquidity_range(42, &target_node_id());
4265		assert_eq!(liquidity_range.unwrap(), (0, 300));
4266
4267		// Now set (overwrite) the scorer state with the external data which should lead to an even greater liquidity
4268		// range. Just the success from the external source is now considered.
4269		combined_scorer.set_scores(external_scores);
4270		let liquidity_range =
4271			combined_scorer.scorer.estimated_channel_liquidity_range(42, &target_node_id());
4272		assert_eq!(liquidity_range.unwrap(), (0, 0));
4273	}
4274
4275	#[test]
4276	#[rustfmt::skip]
4277	fn probes_for_diversity() {
4278		// Tests the probing_diversity_penalty_msat is applied
4279		let logger = TestLogger::new();
4280		let network_graph = network_graph(&logger);
4281		let params = ProbabilisticScoringFeeParameters {
4282			probing_diversity_penalty_msat: 1_000_000,
4283			..ProbabilisticScoringFeeParameters::zero_penalty()
4284		};
4285		let decay_params = ProbabilisticScoringDecayParameters {
4286			liquidity_offset_half_life: Duration::from_secs(10),
4287			..ProbabilisticScoringDecayParameters::zero_penalty()
4288		};
4289		let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
4290		let source = source_node_id();
4291
4292		let usage = ChannelUsage {
4293			amount_msat: 512,
4294			inflight_htlc_msat: 0,
4295			effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
4296		};
4297		let channel = network_graph.read_only().channel(42).unwrap().to_owned();
4298		let (info, _) = channel.as_directed_from(&source).unwrap();
4299		let candidate = CandidateRouteHop::PublicHop(PublicHopCandidate {
4300			info,
4301			short_channel_id: 42,
4302		});
4303
4304		// Initialize the state for channel 42
4305		scorer.payment_path_failed(&payment_path_for_amount(500), 42, Duration::ZERO);
4306
4307		// Apply an update to set the last-update time to 1 second
4308		scorer.payment_path_failed(&payment_path_for_amount(500), 42, Duration::from_secs(1));
4309
4310		// If no time has passed, we get the full probing_diversity_penalty_msat
4311		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 1_000_000);
4312
4313		// As time passes the penalty decreases.
4314		scorer.time_passed(Duration::from_secs(2));
4315		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 999_976);
4316
4317		scorer.time_passed(Duration::from_secs(3));
4318		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 999_953);
4319
4320		// Once we've gotten halfway through the day our penalty is 1/4 the configured value.
4321		scorer.time_passed(Duration::from_secs(86400/2 + 1));
4322		assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 250_000);
4323	}
4324}
4325
4326#[cfg(ldk_bench)]
4327pub mod benches {
4328	use super::*;
4329	use crate::routing::router::bench_utils;
4330	use crate::util::test_utils::TestLogger;
4331	use criterion::Criterion;
4332
4333	#[rustfmt::skip]
4334	pub fn decay_100k_channel_bounds(bench: &mut Criterion) {
4335		let logger = TestLogger::new();
4336		let (_, mut scorer) = bench_utils::read_graph_scorer(&logger).unwrap();
4337		let mut cur_time = Duration::ZERO;
4338			cur_time += Duration::from_millis(1);
4339			scorer.time_passed(cur_time);
4340		bench.bench_function("decay_100k_channel_bounds", |b| b.iter(|| {
4341			cur_time += Duration::from_millis(1);
4342			scorer.time_passed(cur_time);
4343		}));
4344	}
4345}