1use 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
77macro_rules! define_score { ($($supertrait: path)*) => {
89pub trait ScoreLookUp {
95 type ScoreParams;
99 fn channel_penalty_msat(
108 &self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams
109 ) -> u64;
110}
111
112pub trait ScoreUpdate {
114 fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration);
116
117 fn payment_path_successful(&mut self, path: &Path, duration_since_epoch: Duration);
119
120 fn probe_failed(&mut self, path: &Path, short_channel_id: u64, duration_since_epoch: Duration);
122
123 fn probe_successful(&mut self, path: &Path, duration_since_epoch: Duration);
125
126 fn time_passed(&mut self, duration_since_epoch: Duration);
131}
132
133pub 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
184pub trait LockableScore<'a> {
193 type ScoreUpdate: 'a + ScoreUpdate;
195 type ScoreLookUp: 'a + ScoreLookUp;
197
198 type WriteLocked: DerefMut<Target = Self::ScoreUpdate> + Sized;
200
201 type ReadLocked: Deref<Target = Self::ScoreLookUp> + Sized;
203
204 fn read_lock(&'a self) -> Self::ReadLocked;
206
207 fn write_lock(&'a self) -> Self::WriteLocked;
209}
210
211pub 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)]
271pub 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 pub fn new(score: T) -> Self {
306 MultiThreadedLockableScore { score: RwLock::new(score) }
307 }
308}
309
310#[cfg(c_bindings)]
311pub struct MultiThreadedScoreLockRead<'a, T: Score>(RwLockReadGuard<'a, T>);
313
314#[cfg(c_bindings)]
315pub 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#[derive(Clone, Copy, Debug, PartialEq)]
387pub struct ChannelUsage {
388 pub amount_msat: u64,
390
391 pub inflight_htlc_msat: u64,
394
395 pub effective_capacity: EffectiveCapacity,
397}
398
399#[derive(Clone)]
400pub struct FixedPenaltyScorer {
402 penalty_msat: u64,
403}
404
405impl FixedPenaltyScorer {
406 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
451pub 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 last_update_time: Duration,
493}
494#[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 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#[derive(Clone, Debug)]
586pub struct ProbabilisticScoringFeeParameters {
587 pub base_penalty_msat: u64,
597
598 pub base_penalty_amount_multiplier_msat: u64,
618
619 pub liquidity_penalty_multiplier_msat: u64,
641
642 pub liquidity_penalty_amount_multiplier_msat: u64,
667
668 pub historical_liquidity_penalty_multiplier_msat: u64,
684
685 pub historical_liquidity_penalty_amount_multiplier_msat: u64,
707
708 pub manual_node_penalties: HashMap<NodeId, u64>,
714
715 pub anti_probing_penalty_msat: u64,
723
724 pub considered_impossible_penalty_msat: u64,
743
744 pub linear_success_probability: bool,
765
766 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 pub fn add_banned(&mut self, node_id: &NodeId) {
812 self.manual_node_penalties.insert(*node_id, u64::max_value());
813 }
814
815 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 pub fn remove_banned(&mut self, node_id: &NodeId) {
825 self.manual_node_penalties.remove(node_id);
826 }
827
828 pub fn set_manual_penalty(&mut self, node_id: &NodeId, penalty: u64) {
830 self.manual_node_penalties.insert(*node_id, penalty);
831 }
832
833 pub fn remove_manual_penalty(&mut self, node_id: &NodeId) {
835 self.manual_node_penalties.remove(node_id);
836 }
837
838 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#[derive(Copy, Clone, Debug)]
869pub struct ProbabilisticScoringDecayParameters {
870 pub historical_no_updates_half_life: Duration,
882
883 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#[repr(C)] #[derive(Clone)]
931struct ChannelLiquidity {
932 min_liquidity_offset_msat: u64,
934
935 max_liquidity_offset_msat: u64,
937
938 liquidity_history: HistoricalLiquidityTracker,
939
940 last_updated: Duration,
942
943 offset_history_last_updated: Duration,
946
947 last_datapoint_time: Duration,
950}
951
952struct 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 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 #[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 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 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 #[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 max_buckets.reverse();
1108 return Some((min_buckets, max_buckets));
1109 }
1110 }
1111 }
1112 None
1113 }
1114
1115 #[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 ¶ms, 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, ¶ms, min_zero_penalty);
1179 num as f64 / den as f64
1180 }
1181
1182 #[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 pub fn set_scores(&mut self, external_scores: ChannelLiquidities) {
1203 _ = mem::replace(&mut self.channel_liquidities, external_scores);
1204 }
1205
1206 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 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 self.liquidity_history.merge(&other.liquidity_history);
1232 }
1233
1234 #[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 #[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
1298const NEGATIVE_LOG10_UPPER_BOUND: u64 = 2;
1301
1302const PRECISION_LOWER_BOUND_DENOMINATOR: u64 = log_approx::LOWER_BITS_BOUND;
1305
1306const AMOUNT_PENALTY_DIVISOR: u64 = 1 << 20;
1308const BASE_AMOUNT_PENALTY_DIVISOR: u64 = 1 << 30;
1309
1310#[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
1318const 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#[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 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#[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#[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 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 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 #[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 } else if total_inflight_amount_msat >= max_liquidity_msat {
1472 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 } 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 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 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 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 #[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 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 #[inline(always)]
1575 fn min_liquidity_msat(&self) -> u64 {
1576 *self.min_liquidity_offset_msat
1577 }
1578
1579 #[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 #[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 #[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 #[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 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 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 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 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 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
1817pub 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 #[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 pub fn merge(
1858 &mut self, mut external_scores: ChannelLiquidities, duration_since_epoch: Duration,
1859 ) {
1860 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 (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 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 #[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 ((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 #[cfg(test)]
2050 debug_assert!(pos < POSITION_TICKS);
2051 pos
2052 }
2053
2054 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 #[derive(Clone, Copy)]
2079 pub(super) struct HistoricalBucketRangeTracker {
2080 buckets: [u16; 32],
2081 }
2082
2083 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 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 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 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)] pub(super) struct HistoricalLiquidityTracker {
2144 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 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 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 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 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 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 if min_liquidity_offset_history_buckets[0] != 0 {
2335 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 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 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 break;
2377 }
2378
2379 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(¶ms, 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(¶ms, 500, 1000);
2486
2487 assert_ne!(probability1, probability);
2488 }
2489
2490 #[test]
2491 fn historical_heavy_buckets_operations() {
2492 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 (2, self.max_liquidity_offset_msat, required),
2555 (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 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, 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 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 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 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 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 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 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 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 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, ¶ms), 0);
3024 let usage = ChannelUsage { amount_msat: 10_240, ..usage };
3025 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
3026 let usage = ChannelUsage { amount_msat: 102_400, ..usage };
3027 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 47);
3028 let usage = ChannelUsage { amount_msat: 1_023_999, ..usage };
3029 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), 58);
3037 let usage = ChannelUsage { amount_msat: 256, ..usage };
3038 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 125);
3039 let usage = ChannelUsage { amount_msat: 374, ..usage };
3040 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 198);
3041 let usage = ChannelUsage { amount_msat: 512, ..usage };
3042 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 300);
3043 let usage = ChannelUsage { amount_msat: 640, ..usage };
3044 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 425);
3045 let usage = ChannelUsage { amount_msat: 768, ..usage };
3046 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 602);
3047 let usage = ChannelUsage { amount_msat: 896, ..usage };
3048 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), 0);
3088 let usage = ChannelUsage { amount_msat: 50, ..usage };
3089 assert_ne!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
3090 assert_ne!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
3091 let usage = ChannelUsage { amount_msat: 61, ..usage };
3092 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), 301);
3121
3122 scorer.payment_path_failed(&failed_path, 41, Duration::ZERO);
3123 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 301);
3124
3125 scorer.payment_path_successful(&successful_path, Duration::ZERO);
3126 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), 128);
3154 let usage = ChannelUsage { amount_msat: 500, ..usage };
3155 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 301);
3156 let usage = ChannelUsage { amount_msat: 750, ..usage };
3157 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), 0);
3163 let usage = ChannelUsage { amount_msat: 500, ..usage };
3164 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
3165 let usage = ChannelUsage { amount_msat: 750, ..usage };
3166 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), 128);
3195 let usage = ChannelUsage { amount_msat: 500, ..usage };
3196 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 301);
3197 let usage = ChannelUsage { amount_msat: 750, ..usage };
3198 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), 300);
3204 let usage = ChannelUsage { amount_msat: 500, ..usage };
3205 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
3206 let usage = ChannelUsage { amount_msat: 750, ..usage };
3207 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
3208 }
3209
3210 #[test]
3211 #[rustfmt::skip]
3212 fn ignores_channels_after_removed_failed_channel() {
3213 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 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, ¶ms), 128);
3262 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, ¶ms), 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, ¶ms), 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, ¶ms), 80);
3287 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, ¶ms), 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, ¶ms), 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, ¶ms), 128);
3339 assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, ¶ms), 128);
3340 assert_eq!(scorer.channel_penalty_msat(&candidate_43, usage, ¶ms), 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, ¶ms), 128);
3345 assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, ¶ms), 300);
3346 assert_eq!(scorer.channel_penalty_msat(&candidate_43, usage, ¶ms), 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, ¶ms), 0);
3378 let usage = ChannelUsage { amount_msat: 1_023, ..usage };
3379 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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 let usage = ChannelUsage { amount_msat: 128, ..usage };
3386 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
3387 let usage = ChannelUsage { amount_msat: 256, ..usage };
3388 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 93);
3389 let usage = ChannelUsage { amount_msat: 768, ..usage };
3390 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 1_479);
3391 let usage = ChannelUsage { amount_msat: 896, ..usage };
3392 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
3393
3394 scorer.time_passed(Duration::from_secs(5));
3396 let usage = ChannelUsage { amount_msat: 128, ..usage };
3397 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 22);
3398 let usage = ChannelUsage { amount_msat: 256, ..usage };
3399 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 106);
3400 let usage = ChannelUsage { amount_msat: 768, ..usage };
3401 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 921);
3402 let usage = ChannelUsage { amount_msat: 896, ..usage };
3403 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
3404
3405 scorer.time_passed(Duration::from_secs(10));
3407 let usage = ChannelUsage { amount_msat: 64, ..usage };
3408 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
3409 let usage = ChannelUsage { amount_msat: 128, ..usage };
3410 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 34);
3411 let usage = ChannelUsage { amount_msat: 896, ..usage };
3412 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 1_970);
3413 let usage = ChannelUsage { amount_msat: 960, ..usage };
3414 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
3415
3416 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, ¶ms), 0);
3420 let usage = ChannelUsage { amount_msat: 1, ..usage };
3421 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
3422 let usage = ChannelUsage { amount_msat: 1_023, ..usage };
3423 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 2_000);
3424 let usage = ChannelUsage { amount_msat: 1_024, ..usage };
3425 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
3426
3427 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, ¶ms), 0);
3431 let usage = ChannelUsage { amount_msat: 1_024, ..usage };
3432 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), 0);
3437 let usage = ChannelUsage { amount_msat: 1_024, ..usage };
3438 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), 300);
3469
3470 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, ¶ms), 281);
3474
3475 scorer.time_passed(Duration::from_secs(10));
3477 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 291);
3478
3479 scorer.payment_path_successful(&payment_path_for_amount(64), Duration::from_secs(10));
3482 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 331);
3483
3484 scorer.payment_path_failed(&payment_path_for_amount(256), 43, Duration::from_secs(10));
3487 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 245);
3488
3489 scorer.time_passed(Duration::from_secs(20));
3491 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), u64::max_value());
3524
3525 scorer.time_passed(Duration::from_secs(10));
3526 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 300);
3588
3589 deserialized_scorer.time_passed(Duration::from_secs(20));
3590 assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), 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, ¶ms), u64::max_value());
3792
3793 let usage = ChannelUsage { inflight_htlc_msat: 251, ..usage };
3794 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms), base_penalty_msat);
3820
3821 let usage = ChannelUsage { amount_msat: 1_000, ..usage };
3822 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), base_penalty_msat);
3823
3824 let usage = ChannelUsage { amount_msat: 1_001, ..usage };
3825 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms, 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, ¶ms), 2048);
3885 assert_eq!(scorer.channel_penalty_msat(&candidate, usage_1, ¶ms), 220);
3886 }
3887 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, ¶ms, false)
3893 .unwrap() > 0.35);
3894 assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 500, ¶ms, false),
3895 Some(0.0));
3896
3897 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, ¶ms), 83);
3910 }
3911 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 let five_hundred_prob =
3919 scorer.historical_estimated_payment_success_probability(42, &target, 500, ¶ms, 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, ¶ms, false).unwrap();
3924 assert!(one_prob < 0.89, "{}", one_prob);
3925 assert!(one_prob > 0.88, "{}", one_prob);
3926
3927 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, ¶ms), 135);
3940 }
3941 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, ¶ms, 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, ¶ms), 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, ¶ms), 2048);
3970 }
3971
3972 scorer.time_passed(Duration::from_secs(10 * (16 + 60 * 60)));
3974
3975 assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
3978 None);
3979
3980 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 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, ¶ms), 0);
4016
4017 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, ¶ms), 500);
4024
4025 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, ¶ms), 500);
4032
4033 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, ¶ms), 0);
4040 }
4041
4042 #[test]
4043 #[rustfmt::skip]
4044 fn scores_with_blinded_path() {
4045 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, ¶ms), 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 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 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 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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, ¶ms, false),
4135 None);
4136
4137 scorer.payment_path_failed(&payment_path_for_amount(amount_msat), 42, Duration::ZERO);
4139 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms),
4142 2048 + 2048 * amount_msat / super::AMOUNT_PENALTY_DIVISOR);
4143 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 assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, ¶ms, false),
4150 Some(0.0));
4151
4152 amount_msat /= 2;
4154 assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, ¶ms, false),
4157 Some(0.5));
4158
4159 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, ¶ms, 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, ¶ms), 301);
4196
4197 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 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, ¶ms);
4252
4253 combined_scorer.merge(external_scores.clone(), Duration::ZERO);
4254
4255 let penalty_after_merge =
4256 combined_scorer.channel_penalty_msat(&candidate, usage, ¶ms);
4257
4258 assert!(penalty_after_merge < penalty);
4260 }
4261
4262 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 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 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 scorer.payment_path_failed(&payment_path_for_amount(500), 42, Duration::ZERO);
4306
4307 scorer.payment_path_failed(&payment_path_for_amount(500), 42, Duration::from_secs(1));
4309
4310 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 1_000_000);
4312
4313 scorer.time_passed(Duration::from_secs(2));
4315 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 999_976);
4316
4317 scorer.time_passed(Duration::from_secs(3));
4318 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 999_953);
4319
4320 scorer.time_passed(Duration::from_secs(86400/2 + 1));
4322 assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 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}