X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Frouting%2Fscoring.rs;h=38e3c838425f5957fddb0a525dc15ca8cbc24eca;hb=0b7700830b440613d66a4494213b8bbfa63b8f68;hp=206cc0a83a5b0635acf3a98b48ba04fd4e0e4893;hpb=742f5e59b9f6cd24588df02cbb3b84dcc16b693d;p=rust-lightning diff --git a/lightning/src/routing/scoring.rs b/lightning/src/routing/scoring.rs index 206cc0a8..38e3c838 100644 --- a/lightning/src/routing/scoring.rs +++ b/lightning/src/routing/scoring.rs @@ -15,14 +15,14 @@ //! # Example //! //! ``` -//! # extern crate secp256k1; +//! # extern crate bitcoin; //! # //! # use lightning::routing::network_graph::NetworkGraph; //! # use lightning::routing::router::{RouteParameters, find_route}; //! # use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters, Scorer, ScoringParameters}; //! # use lightning::chain::keysinterface::{KeysManager, KeysInterface}; //! # use lightning::util::logger::{Logger, Record}; -//! # use secp256k1::key::PublicKey; +//! # use bitcoin::secp256k1::PublicKey; //! # //! # struct FakeLogger {}; //! # impl Logger for FakeLogger { @@ -33,14 +33,14 @@ //! # //! // Use the default channel penalties. //! let params = ProbabilisticScoringParameters::default(); -//! let scorer = ProbabilisticScorer::new(params, &network_graph); +//! let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); //! //! // Or use custom channel penalties. //! let params = ProbabilisticScoringParameters { //! liquidity_penalty_multiplier_msat: 2 * 1000, //! ..ProbabilisticScoringParameters::default() //! }; -//! let scorer = ProbabilisticScorer::new(params, &network_graph); +//! let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); //! # let random_seed_bytes = [42u8; 32]; //! //! let route = find_route(&payer, &route_params, &network_graph, None, &logger, &scorer, &random_seed_bytes); @@ -55,11 +55,14 @@ //! [`find_route`]: crate::routing::router::find_route use ln::msgs::DecodeError; -use routing::network_graph::{NetworkGraph, NodeId}; +use routing::network_graph::{EffectiveCapacity, NetworkGraph, NodeId}; use routing::router::RouteHop; use util::ser::{Readable, ReadableArgs, Writeable, Writer}; +use util::logger::Logger; +use util::time::Time; use prelude::*; +use core::fmt; use core::cell::{RefCell, RefMut}; use core::ops::{Deref, DerefMut}; use core::time::Duration; @@ -90,7 +93,9 @@ pub trait Score $(: $supertrait)* { /// such as a chain data, network gossip, or invoice hints. For invoice hints, a capacity near /// [`u64::max_value`] is given to indicate sufficient capacity for the invoice's full amount. /// Thus, implementations should be overflow-safe. - fn channel_penalty_msat(&self, short_channel_id: u64, send_amt_msat: u64, capacity_msat: u64, source: &NodeId, target: &NodeId) -> u64; + fn channel_penalty_msat( + &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage + ) -> u64; /// Handles updating channel penalties after failing to route through a channel. fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64); @@ -100,8 +105,10 @@ pub trait Score $(: $supertrait)* { } impl $(+ $supertrait)*> Score for T { - fn channel_penalty_msat(&self, short_channel_id: u64, send_amt_msat: u64, capacity_msat: u64, source: &NodeId, target: &NodeId) -> u64 { - self.deref().channel_penalty_msat(short_channel_id, send_amt_msat, capacity_msat, source, target) + fn channel_penalty_msat( + &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage + ) -> u64 { + self.deref().channel_penalty_msat(short_channel_id, source, target, usage) } fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { @@ -135,6 +142,14 @@ pub trait LockableScore<'a> { fn lock(&'a self) -> Self::Locked; } +/// Refers to a scorer that is accessible under lock and also writeable to disk +/// +/// We need this trait to be able to pass in a scorer to `lightning-background-processor` that will enable us to +/// use the Persister to persist it. +pub trait WriteableScore<'a>: LockableScore<'a> + Writeable {} + +impl<'a, T> WriteableScore<'a> for T where T: LockableScore<'a> + Writeable {} + /// (C-not exported) impl<'a, T: 'a + Score> LockableScore<'a> for Mutex { type Locked = MutexGuard<'a, T>; @@ -191,6 +206,20 @@ impl<'a, S: Writeable> Writeable for MutexGuard<'a, S> { } } +/// Proposed use of a channel passed as a parameter to [`Score::channel_penalty_msat`]. +#[derive(Clone, Copy)] +pub struct ChannelUsage { + /// The amount to send through the channel, denominated in millisatoshis. + pub amount_msat: u64, + + /// Total amount, denominated in millisatoshis, already allocated to send through the channel + /// as part of a multi-path payment. + pub inflight_htlc_msat: u64, + + /// The effective capacity of the channel. + pub effective_capacity: EffectiveCapacity, +} + #[derive(Clone)] /// [`Score`] implementation that uses a fixed penalty. pub struct FixedPenaltyScorer { @@ -205,7 +234,7 @@ impl FixedPenaltyScorer { } impl Score for FixedPenaltyScorer { - fn channel_penalty_msat(&self, _: u64, _: u64, _: u64, _: &NodeId, _: &NodeId) -> u64 { + fn channel_penalty_msat(&self, _: u64, _: &NodeId, _: &NodeId, _: ChannelUsage) -> u64 { self.penalty_msat } @@ -252,7 +281,9 @@ pub type Scorer = ScorerUsingTime::; #[cfg(not(feature = "no-std"))] type ConfiguredTime = std::time::Instant; #[cfg(feature = "no-std")] -type ConfiguredTime = time::Eternity; +use util::time::Eternity; +#[cfg(feature = "no-std")] +type ConfiguredTime = Eternity; // Note that ideally we'd hide ScorerUsingTime from public view by sealing it as well, but rustdoc // doesn't handle this well - instead exposing a `Scorer` which has no trait implementation(s) or @@ -394,13 +425,16 @@ impl Default for ScoringParameters { impl Score for ScorerUsingTime { fn channel_penalty_msat( - &self, short_channel_id: u64, send_amt_msat: u64, capacity_msat: u64, _source: &NodeId, _target: &NodeId + &self, short_channel_id: u64, _source: &NodeId, _target: &NodeId, usage: ChannelUsage ) -> u64 { let failure_penalty_msat = self.channel_failures .get(&short_channel_id) .map_or(0, |value| value.decayed_penalty_msat(self.params.failure_penalty_half_life)); let mut penalty_msat = self.params.base_penalty_msat + failure_penalty_msat; + let send_amt_msat = usage.amount_msat; + let capacity_msat = usage.effective_capacity.as_msat() + .saturating_sub(usage.inflight_htlc_msat); let send_1024ths = send_amt_msat.checked_mul(1024).unwrap_or(u64::max_value()) / capacity_msat; if send_1024ths > self.params.overuse_penalty_start_1024th as u64 { penalty_msat = penalty_msat.checked_add( @@ -503,14 +537,15 @@ impl Readable for ChannelFailure { /// behavior. /// /// [1]: https://arxiv.org/abs/2107.05322 -pub type ProbabilisticScorer = ProbabilisticScorerUsingTime::; +pub type ProbabilisticScorer = ProbabilisticScorerUsingTime::; /// Probabilistic [`Score`] implementation. /// /// (C-not exported) generally all users should use the [`ProbabilisticScorer`] type alias. -pub struct ProbabilisticScorerUsingTime, T: Time> { +pub struct ProbabilisticScorerUsingTime, L: Deref, T: Time> where L::Target: Logger { params: ProbabilisticScoringParameters, network_graph: G, + logger: L, // TODO: Remove entries of closed channels. channel_liquidities: HashMap>, } @@ -603,13 +638,14 @@ struct DirectedChannelLiquidity, T: Time, U: Deref, T: Time> ProbabilisticScorerUsingTime { +impl, L: Deref, T: Time> ProbabilisticScorerUsingTime where L::Target: Logger { /// Creates a new scorer using the given scoring parameters for sending payments from a node /// through a network graph. - pub fn new(params: ProbabilisticScoringParameters, network_graph: G) -> Self { + pub fn new(params: ProbabilisticScoringParameters, network_graph: G, logger: L) -> Self { Self { params, network_graph, + logger, channel_liquidities: HashMap::new(), } } @@ -619,6 +655,33 @@ impl, T: Time> ProbabilisticScorerUsingTime, T: Time, U: Deref> DirectedChannelLiqui impl, T: Time, U: DerefMut> DirectedChannelLiquidity { /// Adjusts the channel liquidity balance bounds when failing to route `amount_msat`. - fn failed_at_channel(&mut self, amount_msat: u64) { + fn failed_at_channel(&mut self, amount_msat: u64, chan_descr: fmt::Arguments, logger: &Log) where Log::Target: Logger { if amount_msat < self.max_liquidity_msat() { + log_debug!(logger, "Setting max liquidity of {} to {}", chan_descr, amount_msat); self.set_max_liquidity_msat(amount_msat); + } else { + log_trace!(logger, "Max liquidity of {} already more than {}", chan_descr, amount_msat); } } /// Adjusts the channel liquidity balance bounds when failing to route `amount_msat` downstream. - fn failed_downstream(&mut self, amount_msat: u64) { + fn failed_downstream(&mut self, amount_msat: u64, chan_descr: fmt::Arguments, logger: &Log) where Log::Target: Logger { if amount_msat > self.min_liquidity_msat() { + log_debug!(logger, "Setting min liquidity of {} to {}", chan_descr, amount_msat); self.set_min_liquidity_msat(amount_msat); + } else { + log_trace!(logger, "Min liquidity of {} already less than {}", chan_descr, amount_msat); } } /// Adjusts the channel liquidity balance bounds when successfully routing `amount_msat`. - fn successful(&mut self, amount_msat: u64) { + fn successful(&mut self, amount_msat: u64, chan_descr: fmt::Arguments, logger: &Log) where Log::Target: Logger { let max_liquidity_msat = self.max_liquidity_msat().checked_sub(amount_msat).unwrap_or(0); + log_debug!(logger, "Subtracting {} from max liquidity of {} (setting it to {})", amount_msat, chan_descr, max_liquidity_msat); self.set_max_liquidity_msat(max_liquidity_msat); } @@ -829,12 +899,22 @@ impl, T: Time, U: DerefMut> DirectedChanne } } -impl, T: Time> Score for ProbabilisticScorerUsingTime { +impl, L: Deref, T: Time> Score for ProbabilisticScorerUsingTime where L::Target: Logger { fn channel_penalty_msat( - &self, short_channel_id: u64, amount_msat: u64, capacity_msat: u64, source: &NodeId, - target: &NodeId + &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage ) -> u64 { + if let EffectiveCapacity::ExactLiquidity { liquidity_msat } = usage.effective_capacity { + if usage.amount_msat > liquidity_msat { + return u64::max_value(); + } else { + return self.params.base_penalty_msat; + }; + } + let liquidity_offset_half_life = self.params.liquidity_offset_half_life; + let amount_msat = usage.amount_msat; + let capacity_msat = usage.effective_capacity.as_msat() + .saturating_sub(usage.inflight_htlc_msat); self.channel_liquidities .get(&short_channel_id) .unwrap_or(&ChannelLiquidity::new()) @@ -845,13 +925,18 @@ impl, T: Time> Score for ProbabilisticScorerUsin fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) { let amount_msat = path.split_last().map(|(hop, _)| hop.fee_msat).unwrap_or(0); let liquidity_offset_half_life = self.params.liquidity_offset_half_life; + log_trace!(self.logger, "Scoring path through to SCID {} as having failed at {} msat", short_channel_id, amount_msat); let network_graph = self.network_graph.read_only(); - for hop in path { + for (hop_idx, hop) in path.iter().enumerate() { let target = NodeId::from_pubkey(&hop.pubkey); let channel_directed_from_source = network_graph.channels() .get(&hop.short_channel_id) .and_then(|channel| channel.as_directed_to(&target)); + if hop.short_channel_id == short_channel_id && hop_idx == 0 { + 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); + } + // Only score announced channels. if let Some((channel, source)) = channel_directed_from_source { let capacity_msat = channel.effective_capacity().as_msat(); @@ -860,7 +945,7 @@ impl, T: Time> Score for ProbabilisticScorerUsin .entry(hop.short_channel_id) .or_insert_with(ChannelLiquidity::new) .as_directed_mut(source, &target, capacity_msat, liquidity_offset_half_life) - .failed_at_channel(amount_msat); + .failed_at_channel(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger); break; } @@ -868,7 +953,10 @@ impl, T: Time> Score for ProbabilisticScorerUsin .entry(hop.short_channel_id) .or_insert_with(ChannelLiquidity::new) .as_directed_mut(source, &target, capacity_msat, liquidity_offset_half_life) - .failed_downstream(amount_msat); + .failed_downstream(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger); + } else { + 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).", + hop.short_channel_id); } } } @@ -876,6 +964,8 @@ impl, T: Time> Score for ProbabilisticScorerUsin fn payment_path_successful(&mut self, path: &[&RouteHop]) { let amount_msat = path.split_last().map(|(hop, _)| hop.fee_msat).unwrap_or(0); let liquidity_offset_half_life = self.params.liquidity_offset_half_life; + log_trace!(self.logger, "Scoring path through SCID {} as having succeeded at {} msat.", + path.split_last().map(|(hop, _)| hop.short_channel_id).unwrap_or(0), amount_msat); let network_graph = self.network_graph.read_only(); for hop in path { let target = NodeId::from_pubkey(&hop.pubkey); @@ -890,7 +980,10 @@ impl, T: Time> Score for ProbabilisticScorerUsin .entry(hop.short_channel_id) .or_insert_with(ChannelLiquidity::new) .as_directed_mut(source, &target, capacity_msat, liquidity_offset_half_life) - .successful(amount_msat); + .successful(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger); + } else { + 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).", + hop.short_channel_id); } } } @@ -1206,7 +1299,7 @@ mod approx { } } -impl, T: Time> Writeable for ProbabilisticScorerUsingTime { +impl, L: Deref, T: Time> Writeable for ProbabilisticScorerUsingTime where L::Target: Logger { #[inline] fn write(&self, w: &mut W) -> Result<(), io::Error> { write_tlv_fields!(w, { @@ -1216,13 +1309,13 @@ impl, T: Time> Writeable for ProbabilisticScorer } } -impl, T: Time> -ReadableArgs<(ProbabilisticScoringParameters, G)> for ProbabilisticScorerUsingTime { +impl, L: Deref, T: Time> +ReadableArgs<(ProbabilisticScoringParameters, G, L)> for ProbabilisticScorerUsingTime where L::Target: Logger { #[inline] fn read( - r: &mut R, args: (ProbabilisticScoringParameters, G) + r: &mut R, args: (ProbabilisticScoringParameters, G, L) ) -> Result { - let (params, network_graph) = args; + let (params, network_graph, logger) = args; let mut channel_liquidities = HashMap::new(); read_tlv_fields!(r, { (0, channel_liquidities, required) @@ -1230,6 +1323,7 @@ ReadableArgs<(ProbabilisticScoringParameters, G)> for ProbabilisticScorerUsingTi Ok(Self { params, network_graph, + logger, channel_liquidities, }) } @@ -1267,170 +1361,28 @@ impl Readable for ChannelLiquidity { } } -pub(crate) mod time { - use core::ops::Sub; - use core::time::Duration; - /// A measurement of time. - pub trait Time: Copy + Sub where Self: Sized { - /// Returns an instance corresponding to the current moment. - fn now() -> Self; - - /// Returns the amount of time elapsed since `self` was created. - fn elapsed(&self) -> Duration; - - /// Returns the amount of time passed between `earlier` and `self`. - fn duration_since(&self, earlier: Self) -> Duration; - - /// Returns the amount of time passed since the beginning of [`Time`]. - /// - /// Used during (de-)serialization. - fn duration_since_epoch() -> Duration; - } - - /// A state in which time has no meaning. - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub struct Eternity; - - #[cfg(not(feature = "no-std"))] - impl Time for std::time::Instant { - fn now() -> Self { - std::time::Instant::now() - } - - fn duration_since(&self, earlier: Self) -> Duration { - self.duration_since(earlier) - } - - fn duration_since_epoch() -> Duration { - use std::time::SystemTime; - SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap() - } - - fn elapsed(&self) -> Duration { - std::time::Instant::elapsed(self) - } - } - - impl Time for Eternity { - fn now() -> Self { - Self - } - - fn duration_since(&self, _earlier: Self) -> Duration { - Duration::from_secs(0) - } - - fn duration_since_epoch() -> Duration { - Duration::from_secs(0) - } - - fn elapsed(&self) -> Duration { - Duration::from_secs(0) - } - } - - impl Sub for Eternity { - type Output = Self; - - fn sub(self, _other: Duration) -> Self { - self - } - } -} - -pub(crate) use self::time::Time; - #[cfg(test)] mod tests { - use super::{ChannelLiquidity, ProbabilisticScoringParameters, ProbabilisticScorerUsingTime, ScoringParameters, ScorerUsingTime, Time}; - use super::time::Eternity; + use super::{ChannelLiquidity, ProbabilisticScoringParameters, ProbabilisticScorerUsingTime, ScoringParameters, ScorerUsingTime}; + use util::time::Time; + use util::time::tests::SinceEpoch; use ln::features::{ChannelFeatures, NodeFeatures}; use ln::msgs::{ChannelAnnouncement, ChannelUpdate, OptionalField, UnsignedChannelAnnouncement, UnsignedChannelUpdate}; - use routing::scoring::Score; - use routing::network_graph::{NetworkGraph, NodeId}; + use routing::scoring::{ChannelUsage, Score}; + use routing::network_graph::{EffectiveCapacity, NetworkGraph, NodeId}; use routing::router::RouteHop; use util::ser::{Readable, ReadableArgs, Writeable}; + use util::test_utils::TestLogger; use bitcoin::blockdata::constants::genesis_block; use bitcoin::hashes::Hash; use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::network::constants::Network; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; - use core::cell::Cell; - use core::ops::Sub; use core::time::Duration; use io; - // `Time` tests - - /// Time that can be advanced manually in tests. - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - struct SinceEpoch(Duration); - - impl SinceEpoch { - thread_local! { - static ELAPSED: Cell = core::cell::Cell::new(Duration::from_secs(0)); - } - - fn advance(duration: Duration) { - Self::ELAPSED.with(|elapsed| elapsed.set(elapsed.get() + duration)) - } - } - - impl Time for SinceEpoch { - fn now() -> Self { - Self(Self::duration_since_epoch()) - } - - fn duration_since(&self, earlier: Self) -> Duration { - self.0 - earlier.0 - } - - fn duration_since_epoch() -> Duration { - Self::ELAPSED.with(|elapsed| elapsed.get()) - } - - fn elapsed(&self) -> Duration { - Self::duration_since_epoch() - self.0 - } - } - - impl Sub for SinceEpoch { - type Output = Self; - - fn sub(self, other: Duration) -> Self { - Self(self.0 - other) - } - } - - #[test] - fn time_passes_when_advanced() { - let now = SinceEpoch::now(); - assert_eq!(now.elapsed(), Duration::from_secs(0)); - - SinceEpoch::advance(Duration::from_secs(1)); - SinceEpoch::advance(Duration::from_secs(1)); - - let elapsed = now.elapsed(); - let later = SinceEpoch::now(); - - assert_eq!(elapsed, Duration::from_secs(2)); - assert_eq!(later - elapsed, now); - } - - #[test] - fn time_never_passes_in_an_eternity() { - let now = Eternity::now(); - let elapsed = now.elapsed(); - let later = Eternity::now(); - - assert_eq!(now.elapsed(), Duration::from_secs(0)); - assert_eq!(later - elapsed, now); - } - - // `Scorer` tests - /// A scorer for testing with time that can be manually advanced. type Scorer = ScorerUsingTime::; @@ -1471,10 +1423,13 @@ mod tests { }); let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + let usage = ChannelUsage { + amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); SinceEpoch::advance(Duration::from_secs(1)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); } #[test] @@ -1488,16 +1443,19 @@ mod tests { }); let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + let usage = ChannelUsage { + amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); scorer.payment_path_failed(&[], 42); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_064); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_064); scorer.payment_path_failed(&[], 42); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_128); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_128); scorer.payment_path_failed(&[], 42); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_192); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_192); } #[test] @@ -1511,25 +1469,28 @@ mod tests { }); let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + let usage = ChannelUsage { + amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); scorer.payment_path_failed(&[], 42); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512); SinceEpoch::advance(Duration::from_secs(9)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512); SinceEpoch::advance(Duration::from_secs(1)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_256); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_256); SinceEpoch::advance(Duration::from_secs(10 * 8)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_001); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_001); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); } #[test] @@ -1543,18 +1504,21 @@ mod tests { }); let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + let usage = ChannelUsage { + amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); scorer.payment_path_failed(&[], 42); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512); // An unchecked right shift 64 bits or more in ChannelFailure::decayed_penalty_msat would // cause an overflow. SinceEpoch::advance(Duration::from_secs(10 * 64)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); } #[test] @@ -1568,19 +1532,22 @@ mod tests { }); let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + let usage = ChannelUsage { + amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); scorer.payment_path_failed(&[], 42); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_256); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_256); scorer.payment_path_failed(&[], 42); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_768); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_768); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_384); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_384); } #[test] @@ -1594,13 +1561,16 @@ mod tests { }); let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000); + let usage = ChannelUsage { + amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000); scorer.payment_path_failed(&[], 42); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_256); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_256); let hop = RouteHop { pubkey: PublicKey::from_slice(target.as_slice()).unwrap(), @@ -1611,10 +1581,10 @@ mod tests { cltv_expiry_delta: 18, }; scorer.payment_path_successful(&[&hop]); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_128); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_128); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_064); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_064); } #[test] @@ -1628,22 +1598,25 @@ mod tests { }); let source = source_node_id(); let target = target_node_id(); + let usage = ChannelUsage { + amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown + }; scorer.payment_path_failed(&[], 42); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_256); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_256); scorer.payment_path_failed(&[], 43); - assert_eq!(scorer.channel_penalty_msat(43, 1, 1, &source, &target), 1_512); + assert_eq!(scorer.channel_penalty_msat(43, &source, &target, usage), 1_512); let mut serialized_scorer = Vec::new(); scorer.write(&mut serialized_scorer).unwrap(); let deserialized_scorer = ::read(&mut io::Cursor::new(&serialized_scorer)).unwrap(); - assert_eq!(deserialized_scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_256); - assert_eq!(deserialized_scorer.channel_penalty_msat(43, 1, 1, &source, &target), 1_512); + assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 1_256); + assert_eq!(deserialized_scorer.channel_penalty_msat(43, &source, &target, usage), 1_512); } #[test] @@ -1657,9 +1630,12 @@ mod tests { }); let source = source_node_id(); let target = target_node_id(); + let usage = ChannelUsage { + amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown + }; scorer.payment_path_failed(&[], 42); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512); let mut serialized_scorer = Vec::new(); scorer.write(&mut serialized_scorer).unwrap(); @@ -1667,10 +1643,10 @@ mod tests { SinceEpoch::advance(Duration::from_secs(10)); let deserialized_scorer = ::read(&mut io::Cursor::new(&serialized_scorer)).unwrap(); - assert_eq!(deserialized_scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_256); + assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 1_256); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(deserialized_scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_128); + assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 1_128); } #[test] @@ -1685,17 +1661,30 @@ mod tests { let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 1_000, 1_024_000, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 256_999, 1_024_000, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 257_000, 1_024_000, &source, &target), 100); - assert_eq!(scorer.channel_penalty_msat(42, 258_000, 1_024_000, &source, &target), 200); - assert_eq!(scorer.channel_penalty_msat(42, 512_000, 1_024_000, &source, &target), 256 * 100); + let usage = ChannelUsage { + amount_msat: 1_000, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000 }, + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + + let usage = ChannelUsage { amount_msat: 256_999, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + + let usage = ChannelUsage { amount_msat: 257_000, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 100); + + let usage = ChannelUsage { amount_msat: 258_000, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 200); + + let usage = ChannelUsage { amount_msat: 512_000, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 256 * 100); } // `ProbabilisticScorer` tests /// A probabilistic scorer for testing with time that can be manually advanced. - type ProbabilisticScorer<'a> = ProbabilisticScorerUsingTime::<&'a NetworkGraph, SinceEpoch>; + type ProbabilisticScorer<'a> = ProbabilisticScorerUsingTime::<&'a NetworkGraph, &'a TestLogger, SinceEpoch>; fn sender_privkey() -> SecretKey { SecretKey::from_slice(&[41; 32]).unwrap() @@ -1752,10 +1741,10 @@ mod tests { }; let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]); let signed_announcement = ChannelAnnouncement { - node_signature_1: secp_ctx.sign(&msghash, &node_1_key), - node_signature_2: secp_ctx.sign(&msghash, &node_2_key), - bitcoin_signature_1: secp_ctx.sign(&msghash, &node_1_secret), - bitcoin_signature_2: secp_ctx.sign(&msghash, &node_2_secret), + node_signature_1: secp_ctx.sign_ecdsa(&msghash, &node_1_key), + node_signature_2: secp_ctx.sign_ecdsa(&msghash, &node_2_key), + bitcoin_signature_1: secp_ctx.sign_ecdsa(&msghash, &node_1_secret), + bitcoin_signature_2: secp_ctx.sign_ecdsa(&msghash, &node_2_secret), contents: unsigned_announcement, }; let chain_source: Option<&::util::test_utils::TestChainSource> = None; @@ -1784,7 +1773,7 @@ mod tests { }; let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_update.encode()[..])[..]); let signed_update = ChannelUpdate { - signature: secp_ctx.sign(&msghash, &node_key), + signature: secp_ctx.sign_ecdsa(&msghash, &node_key), contents: unsigned_update, }; network_graph.update_channel(&signed_update, &secp_ctx).unwrap(); @@ -1821,10 +1810,11 @@ mod tests { #[test] fn liquidity_bounds_directed_from_lowest_node_id() { + let logger = TestLogger::new(); let last_updated = SinceEpoch::now(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters::default(); - let mut scorer = ProbabilisticScorer::new(params, &network_graph) + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, ChannelLiquidity { min_liquidity_offset_msat: 700, max_liquidity_offset_msat: 100, last_updated @@ -1895,10 +1885,11 @@ mod tests { #[test] fn resets_liquidity_upper_bound_when_crossed_by_lower_bound() { + let logger = TestLogger::new(); let last_updated = SinceEpoch::now(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters::default(); - let mut scorer = ProbabilisticScorer::new(params, &network_graph) + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, ChannelLiquidity { min_liquidity_offset_msat: 200, max_liquidity_offset_msat: 400, last_updated @@ -1952,10 +1943,11 @@ mod tests { #[test] fn resets_liquidity_lower_bound_when_crossed_by_upper_bound() { + let logger = TestLogger::new(); let last_updated = SinceEpoch::now(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters::default(); - let mut scorer = ProbabilisticScorer::new(params, &network_graph) + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, ChannelLiquidity { min_liquidity_offset_msat: 200, max_liquidity_offset_msat: 400, last_updated @@ -2009,38 +2001,59 @@ mod tests { #[test] fn increased_penalty_nearing_liquidity_upper_bound() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024_000, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 10_240, 1_024_000, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 102_400, 1_024_000, &source, &target), 47); - assert_eq!(scorer.channel_penalty_msat(42, 1_024_000, 1_024_000, &source, &target), 2_000); - - assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 58); - assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125); - assert_eq!(scorer.channel_penalty_msat(42, 374, 1_024, &source, &target), 198); - assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 300); - assert_eq!(scorer.channel_penalty_msat(42, 640, 1_024, &source, &target), 425); - assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 602); - assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 902); + let usage = ChannelUsage { + amount_msat: 1_024, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000 }, + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 10_240, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 102_400, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 47); + let usage = ChannelUsage { amount_msat: 1_024_000, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000); + + let usage = ChannelUsage { + amount_msat: 128, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 }, + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58); + let usage = ChannelUsage { amount_msat: 256, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125); + let usage = ChannelUsage { amount_msat: 374, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 198); + let usage = ChannelUsage { amount_msat: 512, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); + let usage = ChannelUsage { amount_msat: 640, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 425); + let usage = ChannelUsage { amount_msat: 768, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602); + let usage = ChannelUsage { amount_msat: 896, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 902); } #[test] fn constant_penalty_outside_liquidity_bounds() { + let logger = TestLogger::new(); let last_updated = SinceEpoch::now(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph) + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger) .with_channel(42, ChannelLiquidity { min_liquidity_offset_msat: 40, max_liquidity_offset_msat: 40, last_updated @@ -2048,264 +2061,356 @@ mod tests { let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 39, 100, &source, &target), 0); - assert_ne!(scorer.channel_penalty_msat(42, 50, 100, &source, &target), 0); - assert_ne!(scorer.channel_penalty_msat(42, 50, 100, &source, &target), u64::max_value()); - assert_eq!(scorer.channel_penalty_msat(42, 61, 100, &source, &target), u64::max_value()); + let usage = ChannelUsage { + amount_msat: 39, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 100 }, + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 50, ..usage }; + assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); + let usage = ChannelUsage { amount_msat: 61, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); } #[test] fn does_not_further_penalize_own_channel() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let sender = sender_node_id(); let source = source_node_id(); + let usage = ChannelUsage { + amount_msat: 500, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 }, + }; let failed_path = payment_path_for_amount(500); let successful_path = payment_path_for_amount(200); - assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 301); + assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301); scorer.payment_path_failed(&failed_path.iter().collect::>(), 41); - assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 301); + assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301); scorer.payment_path_successful(&successful_path.iter().collect::>()); - assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 301); + assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301); } #[test] fn sets_liquidity_lower_bound_on_downstream_failure() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); let path = payment_path_for_amount(500); - assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 128); - assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 301); - assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 602); + let usage = ChannelUsage { + amount_msat: 250, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 }, + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128); + let usage = ChannelUsage { amount_msat: 500, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 301); + let usage = ChannelUsage { amount_msat: 750, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602); scorer.payment_path_failed(&path.iter().collect::>(), 43); - assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 300); + let usage = ChannelUsage { amount_msat: 250, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 500, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 750, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); } #[test] fn sets_liquidity_upper_bound_on_failure() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); let path = payment_path_for_amount(500); - assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 128); - assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 301); - assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 602); + let usage = ChannelUsage { + amount_msat: 250, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 }, + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128); + let usage = ChannelUsage { amount_msat: 500, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 301); + let usage = ChannelUsage { amount_msat: 750, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602); scorer.payment_path_failed(&path.iter().collect::>(), 42); - assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 300); - assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), u64::max_value()); - assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), u64::max_value()); + let usage = ChannelUsage { amount_msat: 250, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); + let usage = ChannelUsage { amount_msat: 500, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); + let usage = ChannelUsage { amount_msat: 750, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); } #[test] fn reduces_liquidity_upper_bound_along_path_on_success() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let sender = sender_node_id(); let source = source_node_id(); let target = target_node_id(); let recipient = recipient_node_id(); + let usage = ChannelUsage { + amount_msat: 250, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 }, + }; let path = payment_path_for_amount(500); - assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 128); - assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 128); - assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 128); + assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 128); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128); + assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage), 128); scorer.payment_path_successful(&path.iter().collect::>()); - assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 128); - assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 300); - assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 300); + assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 128); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); + assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage), 300); } #[test] fn decays_liquidity_bounds_over_time() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 0, 1_024, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024, &source, &target), 2_000); + let usage = ChannelUsage { + amount_msat: 0, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 }, + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 1_024, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000); scorer.payment_path_failed(&payment_path_for_amount(768).iter().collect::>(), 42); scorer.payment_path_failed(&payment_path_for_amount(128).iter().collect::>(), 43); - assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 93); - assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_479); - assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), u64::max_value()); + let usage = ChannelUsage { amount_msat: 128, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 256, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 93); + let usage = ChannelUsage { amount_msat: 768, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_479); + let usage = ChannelUsage { amount_msat: 896, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); SinceEpoch::advance(Duration::from_secs(9)); - assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 93); - assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_479); - assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), u64::max_value()); + let usage = ChannelUsage { amount_msat: 128, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 256, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 93); + let usage = ChannelUsage { amount_msat: 768, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_479); + let usage = ChannelUsage { amount_msat: 896, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); SinceEpoch::advance(Duration::from_secs(1)); - assert_eq!(scorer.channel_penalty_msat(42, 64, 1_024, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 34); - assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 1_970); - assert_eq!(scorer.channel_penalty_msat(42, 960, 1_024, &source, &target), u64::max_value()); + let usage = ChannelUsage { amount_msat: 64, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 128, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 34); + let usage = ChannelUsage { amount_msat: 896, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_970); + let usage = ChannelUsage { amount_msat: 960, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); // Fully decay liquidity lower bound. SinceEpoch::advance(Duration::from_secs(10 * 7)); - assert_eq!(scorer.channel_penalty_msat(42, 0, 1_024, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 1, 1_024, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 1_023, 1_024, &source, &target), 2_000); - assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024, &source, &target), 2_000); + let usage = ChannelUsage { amount_msat: 0, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 1, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 1_023, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000); + let usage = ChannelUsage { amount_msat: 1_024, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000); // Fully decay liquidity upper bound. SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 0, 1_024, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024, &source, &target), 2_000); + let usage = ChannelUsage { amount_msat: 0, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 1_024, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 0, 1_024, &source, &target), 0); - assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024, &source, &target), 2_000); + let usage = ChannelUsage { amount_msat: 0, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0); + let usage = ChannelUsage { amount_msat: 1_024, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000); } #[test] fn decays_liquidity_bounds_without_shift_overflow() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125); + let usage = ChannelUsage { + amount_msat: 256, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 }, + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125); scorer.payment_path_failed(&payment_path_for_amount(512).iter().collect::>(), 42); - assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 281); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 281); // An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat // would cause an overflow. SinceEpoch::advance(Duration::from_secs(10 * 64)); - assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125); } #[test] fn restricts_liquidity_bounds_after_decay() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); + let usage = ChannelUsage { + amount_msat: 512, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 }, + }; - assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 300); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); // More knowledge gives higher confidence (256, 768), meaning a lower penalty. scorer.payment_path_failed(&payment_path_for_amount(768).iter().collect::>(), 42); scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::>(), 43); - assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 281); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 281); // Decaying knowledge gives less confidence (128, 896), meaning a higher penalty. SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 291); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 291); // Reducing the upper bound gives more confidence (128, 832) that the payment amount (512) // is closer to the upper bound, meaning a higher penalty. scorer.payment_path_successful(&payment_path_for_amount(64).iter().collect::>()); - assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 331); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 331); // Increasing the lower bound gives more confidence (256, 832) that the payment amount (512) // is closer to the lower bound, meaning a lower penalty. scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::>(), 43); - assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 245); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 245); // Further decaying affects the lower bound more than the upper bound (128, 928). SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 280); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 280); } #[test] fn restores_persisted_liquidity_bounds() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); + let usage = ChannelUsage { + amount_msat: 500, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 }, + }; scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::>(), 42); - assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), u64::max_value()); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 473); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 473); scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::>(), 43); - assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); let mut serialized_scorer = Vec::new(); scorer.write(&mut serialized_scorer).unwrap(); let mut serialized_scorer = io::Cursor::new(&serialized_scorer); let deserialized_scorer = - ::read(&mut serialized_scorer, (params, &network_graph)).unwrap(); - assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300); + ::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap(); + assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 300); } #[test] fn decays_persisted_liquidity_bounds() { + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, liquidity_offset_half_life: Duration::from_secs(10), ..ProbabilisticScoringParameters::zero_penalty() }; - let mut scorer = ProbabilisticScorer::new(params, &network_graph); + let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); + let usage = ChannelUsage { + amount_msat: 500, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 }, + }; scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::>(), 42); - assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), u64::max_value()); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); let mut serialized_scorer = Vec::new(); scorer.write(&mut serialized_scorer).unwrap(); @@ -2314,96 +2419,191 @@ mod tests { let mut serialized_scorer = io::Cursor::new(&serialized_scorer); let deserialized_scorer = - ::read(&mut serialized_scorer, (params, &network_graph)).unwrap(); - assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 473); + ::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap(); + assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 473); scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::>(), 43); - assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); SinceEpoch::advance(Duration::from_secs(10)); - assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 365); + assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 365); } #[test] fn scores_realistic_payments() { // Shows the scores of "realistic" sends of 100k sats over channels of 1-10m sats (with a // 50k sat reserve). + let logger = TestLogger::new(); let network_graph = network_graph(); let params = ProbabilisticScoringParameters::default(); - let scorer = ProbabilisticScorer::new(params, &network_graph); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); let source = source_node_id(); let target = target_node_id(); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 950_000_000, &source, &target), 3613); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 1_950_000_000, &source, &target), 1977); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 2_950_000_000, &source, &target), 1474); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 3_950_000_000, &source, &target), 1223); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 4_950_000_000, &source, &target), 877); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 5_950_000_000, &source, &target), 845); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 6_950_000_000, &source, &target), 500); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 7_450_000_000, &source, &target), 500); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 7_950_000_000, &source, &target), 500); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 8_950_000_000, &source, &target), 500); - assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 9_950_000_000, &source, &target), 500); + let usage = ChannelUsage { + amount_msat: 100_000_000, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 950_000_000 }, + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 3613); + let usage = ChannelUsage { + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_950_000_000 }, ..usage + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1977); + let usage = ChannelUsage { + effective_capacity: EffectiveCapacity::Total { capacity_msat: 2_950_000_000 }, ..usage + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1474); + let usage = ChannelUsage { + effective_capacity: EffectiveCapacity::Total { capacity_msat: 3_950_000_000 }, ..usage + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1223); + let usage = ChannelUsage { + effective_capacity: EffectiveCapacity::Total { capacity_msat: 4_950_000_000 }, ..usage + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 877); + let usage = ChannelUsage { + effective_capacity: EffectiveCapacity::Total { capacity_msat: 5_950_000_000 }, ..usage + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 845); + let usage = ChannelUsage { + effective_capacity: EffectiveCapacity::Total { capacity_msat: 6_950_000_000 }, ..usage + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500); + let usage = ChannelUsage { + effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_450_000_000 }, ..usage + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500); + let usage = ChannelUsage { + effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_950_000_000 }, ..usage + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500); + let usage = ChannelUsage { + effective_capacity: EffectiveCapacity::Total { capacity_msat: 8_950_000_000 }, ..usage + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500); + let usage = ChannelUsage { + effective_capacity: EffectiveCapacity::Total { capacity_msat: 9_950_000_000 }, ..usage + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500); } #[test] fn adds_base_penalty_to_liquidity_penalty() { + let logger = TestLogger::new(); let network_graph = network_graph(); let source = source_node_id(); let target = target_node_id(); + let usage = ChannelUsage { + amount_msat: 128, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 }, + }; let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); - assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 58); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58); let params = ProbabilisticScoringParameters { base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000, ..Default::default() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); - assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 558); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 558); } #[test] fn adds_amount_penalty_to_liquidity_penalty() { + let logger = TestLogger::new(); let network_graph = network_graph(); let source = source_node_id(); let target = target_node_id(); + let usage = ChannelUsage { + amount_msat: 512_000, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000 }, + }; let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, amount_penalty_multiplier_msat: 0, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); - assert_eq!(scorer.channel_penalty_msat(42, 512_000, 1_024_000, &source, &target), 300); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300); let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 1_000, amount_penalty_multiplier_msat: 256, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); - assert_eq!(scorer.channel_penalty_msat(42, 512_000, 1_024_000, &source, &target), 337); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 337); } #[test] fn calculates_log10_without_overflowing_u64_max_value() { + let logger = TestLogger::new(); let network_graph = network_graph(); let source = source_node_id(); let target = target_node_id(); + let usage = ChannelUsage { + amount_msat: u64::max_value(), + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Infinite, + }; let params = ProbabilisticScoringParameters { liquidity_penalty_multiplier_msat: 40_000, ..ProbabilisticScoringParameters::zero_penalty() }; - let scorer = ProbabilisticScorer::new(params, &network_graph); - assert_eq!( - scorer.channel_penalty_msat(42, u64::max_value(), u64::max_value(), &source, &target), - 80_000, - ); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 80_000); + } + + #[test] + fn accounts_for_inflight_htlc_usage() { + let network_graph = network_graph(); + let logger = TestLogger::new(); + let params = ProbabilisticScoringParameters::default(); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); + let source = source_node_id(); + let target = target_node_id(); + + let usage = ChannelUsage { + amount_msat: 750, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 }, + }; + assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); + + let usage = ChannelUsage { inflight_htlc_msat: 251, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); + } + + #[test] + fn removes_uncertainity_when_exact_liquidity_known() { + let network_graph = network_graph(); + let logger = TestLogger::new(); + let params = ProbabilisticScoringParameters::default(); + let scorer = ProbabilisticScorer::new(params, &network_graph, &logger); + let source = source_node_id(); + let target = target_node_id(); + + let base_penalty_msat = params.base_penalty_msat; + let usage = ChannelUsage { + amount_msat: 750, + inflight_htlc_msat: 0, + effective_capacity: EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, + }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), base_penalty_msat); + + let usage = ChannelUsage { amount_msat: 1_000, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), base_penalty_msat); + + let usage = ChannelUsage { amount_msat: 1_001, ..usage }; + assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value()); } }