]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Pass `EffectiveCapacity` through to scorer instead of a `u64`
authorMatt Corallo <git@bluematt.me>
Thu, 24 Mar 2022 05:52:17 +0000 (05:52 +0000)
committerMatt Corallo <git@bluematt.me>
Thu, 24 Mar 2022 17:16:41 +0000 (17:16 +0000)
There is little reason to take the `EffectiveCapacity`, which has
a bunch of information about the type of channel, and distill it
down to a `u64` when scoring channels.

Instead, we here pass the full information we know, in the form of
the original `EffectiveCapacity`. This does create more branching
in the main router loop, which appears to have a very slight (1-2%)
performance loss, but that may well be within noise.

Much more importantly, this resolves a panic in our log
approximation where we can accidentally call `log(0)` when the
channel's effective capacity is `u64::max_value()`.

lightning-invoice/src/payment.rs
lightning/src/routing/network_graph.rs
lightning/src/routing/router.rs
lightning/src/routing/scoring.rs

index 82c07199f0db871c1498fb64a21e63603aec2084..955e2dc6a6b141155a11542d6bbe20de4c92709b 100644 (file)
@@ -39,7 +39,7 @@
 //! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
 //! # use lightning::ln::msgs::LightningError;
 //! # use lightning::routing::scoring::Score;
-//! # use lightning::routing::network_graph::NodeId;
+//! # use lightning::routing::network_graph::{EffectiveCapacity, NodeId};
 //! # use lightning::routing::router::{Route, RouteHop, RouteParameters};
 //! # use lightning::util::events::{Event, EventHandler, EventsProvider};
 //! # use lightning::util::logger::{Logger, Record};
@@ -90,7 +90,7 @@
 //! # }
 //! # impl Score for FakeScorer {
 //! #     fn channel_penalty_msat(
-//! #         &self, _short_channel_id: u64, _send_amt: u64, _chan_amt: u64, _source: &NodeId, _target: &NodeId
+//! #         &self, _short_channel_id: u64, _send_amt: u64, _cap: EffectiveCapacity, _source: &NodeId, _target: &NodeId
 //! #     ) -> u64 { 0 }
 //! #     fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
 //! #     fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
@@ -532,7 +532,7 @@ mod tests {
        use lightning::ln::features::{ChannelFeatures, NodeFeatures, InitFeatures};
        use lightning::ln::functional_test_utils::*;
        use lightning::ln::msgs::{ChannelMessageHandler, ErrorAction, LightningError};
-       use lightning::routing::network_graph::NodeId;
+       use lightning::routing::network_graph::{EffectiveCapacity, NodeId};
        use lightning::routing::router::{PaymentParameters, Route, RouteHop};
        use lightning::util::test_utils::TestLogger;
        use lightning::util::errors::APIError;
@@ -1327,7 +1327,7 @@ mod tests {
 
        impl Score for TestScorer {
                fn channel_penalty_msat(
-                       &self, _short_channel_id: u64, _send_amt: u64, _chan_amt: u64, _source: &NodeId, _target: &NodeId
+                       &self, _short_channel_id: u64, _send_amt: u64, _capacity: EffectiveCapacity, _source: &NodeId, _target: &NodeId
                ) -> u64 { 0 }
 
                fn payment_path_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) {
index 012357df361767e330a698f492846a6b44e8917b..2f1dee919900ece84b879a7eb336fad9c2510dfe 100644 (file)
@@ -758,7 +758,7 @@ impl<'a> DirectedChannelInfo<'a> {
                        })
                        .or_else(|| capacity_msat.map(|capacity_msat|
                                        EffectiveCapacity::Total { capacity_msat }))
-                       .unwrap_or(EffectiveCapacity::Unknown)
+                       .unwrap_or(EffectiveCapacity::Unknown { previously_used_msat: 0 })
        }
 
        /// Returns `Some` if [`ChannelUpdateInfo`] is available in the direction.
@@ -807,7 +807,8 @@ impl<'a> fmt::Debug for DirectedChannelInfoWithUpdate<'a> {
 /// The effective capacity of a channel for routing purposes.
 ///
 /// While this may be smaller than the actual channel capacity, amounts greater than
-/// [`Self::as_msat`] should not be routed through the channel.
+/// [`Self::as_msat_with_default`] should not be routed through the channel.
+#[derive(Copy, Clone, PartialEq)]
 pub enum EffectiveCapacity {
        /// The available liquidity in the channel known from being a channel counterparty, and thus a
        /// direct hop.
@@ -831,7 +832,11 @@ pub enum EffectiveCapacity {
        Infinite,
        /// A capacity that is unknown possibly because either the chain state is unavailable to know
        /// the total capacity or the `htlc_maximum_msat` was not advertised on the gossip network.
-       Unknown,
+       Unknown {
+               /// An amount which should be considered "already used". Used during routing to keep track
+               /// of how much we're already considering routing through this channel.
+               previously_used_msat: u64,
+       },
 }
 
 /// The presumed channel capacity denominated in millisatoshi for [`EffectiveCapacity::Unknown`] to
@@ -839,14 +844,53 @@ pub enum EffectiveCapacity {
 pub const UNKNOWN_CHANNEL_CAPACITY_MSAT: u64 = 250_000 * 1000;
 
 impl EffectiveCapacity {
-       /// Returns the effective capacity denominated in millisatoshi.
-       pub fn as_msat(&self) -> u64 {
+       #[inline]
+       pub(crate) fn as_msat_without_bounds(&self) -> u64 {
                match self {
                        EffectiveCapacity::ExactLiquidity { liquidity_msat } => *liquidity_msat,
                        EffectiveCapacity::MaximumHTLC { amount_msat } => *amount_msat,
                        EffectiveCapacity::Total { capacity_msat } => *capacity_msat,
                        EffectiveCapacity::Infinite => u64::max_value(),
-                       EffectiveCapacity::Unknown => UNKNOWN_CHANNEL_CAPACITY_MSAT,
+                       EffectiveCapacity::Unknown { previously_used_msat } => UNKNOWN_CHANNEL_CAPACITY_MSAT.saturating_sub(*previously_used_msat),
+               }
+       }
+
+       /// Returns the effective capacity denominated in millisatoshi.
+       ///
+       /// Returns [`UNKNOWN_CHANNEL_CAPACITY_MSAT`] minus the `previously_used_msat` for
+       /// [`EffectiveCapacity::Unknown`].
+       #[inline]
+       pub fn as_msat_with_default(&self) -> Option<u64> {
+               match self {
+                       EffectiveCapacity::ExactLiquidity { liquidity_msat } => Some(*liquidity_msat),
+                       EffectiveCapacity::MaximumHTLC { amount_msat } => Some(*amount_msat),
+                       EffectiveCapacity::Total { capacity_msat } => Some(*capacity_msat),
+                       EffectiveCapacity::Infinite => None,
+                       EffectiveCapacity::Unknown { previously_used_msat } => Some(UNKNOWN_CHANNEL_CAPACITY_MSAT.saturating_sub(*previously_used_msat)),
+               }
+       }
+
+       /// Returns a new [`EffectiveCapacity`] which is reduced by the given number of millisatoshis
+       #[inline]
+       pub fn checked_sub(&self, msats: u64) -> Option<EffectiveCapacity> {
+               match self {
+                       Self::ExactLiquidity { liquidity_msat } => match liquidity_msat.checked_sub(msats) {
+                               Some(liquidity_msat) => Some(Self::ExactLiquidity { liquidity_msat }),
+                               None => None,
+                       },
+                       Self::MaximumHTLC { amount_msat } => match amount_msat.checked_sub(msats) {
+                               Some(amount_msat) => Some(Self::MaximumHTLC { amount_msat }),
+                               None => None,
+                       },
+                       Self::Total { capacity_msat } => match capacity_msat.checked_sub(msats) {
+                               Some(capacity_msat) => Some(Self::Total { capacity_msat }),
+                               None => None,
+                       },
+                       Self::Infinite => Some(Self::Infinite),
+                       Self::Unknown { previously_used_msat } => match previously_used_msat.checked_add(msats) {
+                               Some(previously_used_msat) => Some(Self::Unknown { previously_used_msat }),
+                               None => None,
+                       },
                }
        }
 }
index 677a571620ce31a329f8b046d294a8d99b1c2387..fbdefb9840a80d4c38ebc32007e8b39ab6d31ed8 100644 (file)
@@ -854,7 +854,7 @@ where L::Target: Logger {
                                let short_channel_id = $candidate.short_channel_id();
                                let available_liquidity_msat = bookkept_channels_liquidity_available_msat
                                        .entry(short_channel_id)
-                                       .or_insert_with(|| $candidate.effective_capacity().as_msat());
+                                       .or_insert_with(|| $candidate.effective_capacity());
 
                                // It is tricky to substract $next_hops_fee_msat from available liquidity here.
                                // It may be misleading because we might later choose to reduce the value transferred
@@ -863,7 +863,7 @@ where L::Target: Logger {
                                // fees caused by one expensive channel, but then this channel could have been used
                                // if the amount being transferred over this path is lower.
                                // We do this for now, but this is a subject for removal.
-                               if let Some(available_value_contribution_msat) = available_liquidity_msat.checked_sub($next_hops_fee_msat) {
+                               if let Some(available_value_contribution) = available_liquidity_msat.checked_sub($next_hops_fee_msat) {
 
                                        // Routing Fragmentation Mitigation heuristic:
                                        //
@@ -886,6 +886,7 @@ where L::Target: Logger {
                                                final_value_msat
                                        };
                                        // Verify the liquidity offered by this channel complies to the minimal contribution.
+                                       let available_value_contribution_msat = available_value_contribution.as_msat_without_bounds();
                                        let contributes_sufficient_value = available_value_contribution_msat >= minimal_value_contribution_msat;
 
                                        // Do not consider candidates that exceed the maximum total cltv expiry limit.
@@ -1220,9 +1221,8 @@ where L::Target: Logger {
                                                        short_channel_id: hop.short_channel_id,
                                                })
                                                .unwrap_or_else(|| CandidateRouteHop::PrivateHop { hint: hop });
-                                       let capacity_msat = candidate.effective_capacity().as_msat();
                                        aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
-                                               .checked_add(scorer.channel_penalty_msat(hop.short_channel_id, final_value_msat, capacity_msat, &source, &target))
+                                               .checked_add(scorer.channel_penalty_msat(hop.short_channel_id, path_value_msat, candidate.effective_capacity(), &source, &target))
                                                .unwrap_or_else(|| u64::max_value());
 
                                        aggregate_next_hops_cltv_delta = aggregate_next_hops_cltv_delta
@@ -1382,12 +1382,13 @@ where L::Target: Logger {
                                        let mut spent_on_hop_msat = value_contribution_msat;
                                        let next_hops_fee_msat = payment_hop.next_hops_fee_msat;
                                        spent_on_hop_msat += next_hops_fee_msat;
-                                       if spent_on_hop_msat == *channel_liquidity_available_msat {
+                                       if spent_on_hop_msat == channel_liquidity_available_msat.as_msat_without_bounds() {
                                                // If this path used all of this channel's available liquidity, we know
                                                // this path will not be selected again in the next loop iteration.
                                                prevented_redundant_path_selection = true;
                                        }
-                                       *channel_liquidity_available_msat -= spent_on_hop_msat;
+                                       *channel_liquidity_available_msat = channel_liquidity_available_msat
+                                               .checked_sub(spent_on_hop_msat).unwrap_or(EffectiveCapacity::ExactLiquidity { liquidity_msat: 0 });
                                }
                                if !prevented_redundant_path_selection {
                                        // If we weren't capped by hitting a liquidity limit on a channel in the path,
@@ -1396,7 +1397,7 @@ where L::Target: Logger {
                                        let victim_scid = payment_path.hops[(payment_path.hops.len() - 1) / 2].0.candidate.short_channel_id();
                                        log_trace!(logger, "Disabling channel {} for future path building iterations to avoid duplicates.", victim_scid);
                                        let victim_liquidity = bookkept_channels_liquidity_available_msat.get_mut(&victim_scid).unwrap();
-                                       *victim_liquidity = 0;
+                                       *victim_liquidity = EffectiveCapacity::ExactLiquidity { liquidity_msat: 0 };
                                }
 
                                // Track the total amount all our collected paths allow to send so that we:
@@ -1664,7 +1665,7 @@ fn add_random_cltv_offset(route: &mut Route, payment_params: &PaymentParameters,
 
 #[cfg(test)]
 mod tests {
-       use routing::network_graph::{NetworkGraph, NetGraphMsgHandler, NodeId};
+       use routing::network_graph::{EffectiveCapacity, NetworkGraph, NetGraphMsgHandler, NodeId};
        use routing::router::{get_route, add_random_cltv_offset, PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA};
        use routing::scoring::Score;
        use chain::transaction::OutPoint;
@@ -5001,7 +5002,7 @@ mod tests {
                fn write<W: Writer>(&self, _w: &mut W) -> Result<(), ::io::Error> { unimplemented!() }
        }
        impl Score for BadChannelScorer {
-               fn channel_penalty_msat(&self, short_channel_id: u64, _send_amt: u64, _capacity_msat: u64, _source: &NodeId, _target: &NodeId) -> u64 {
+               fn channel_penalty_msat(&self, short_channel_id: u64, _send_amt: u64, _capacity: EffectiveCapacity, _source: &NodeId, _target: &NodeId) -> u64 {
                        if short_channel_id == self.short_channel_id { u64::max_value() } else { 0 }
                }
 
@@ -5019,7 +5020,7 @@ mod tests {
        }
 
        impl Score for BadNodeScorer {
-               fn channel_penalty_msat(&self, _short_channel_id: u64, _send_amt: u64, _capacity_msat: u64, _source: &NodeId, target: &NodeId) -> u64 {
+               fn channel_penalty_msat(&self, _short_channel_id: u64, _send_amt: u64, _capacity: EffectiveCapacity, _source: &NodeId, target: &NodeId) -> u64 {
                        if *target == self.node_id { u64::max_value() } else { 0 }
                }
 
index d8b1740c19ec2f1ec6098fb5f941c5e387845865..1d1c27a84e08e7e4ce34c91553b098eff214316c 100644 (file)
@@ -55,7 +55,7 @@
 //! [`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};
 
@@ -86,11 +86,11 @@ pub trait Score $(: $supertrait)* {
        /// given channel in the direction from `source` to `target`.
        ///
        /// The channel's capacity (less any other MPP parts that are also being considered for use in
-       /// the same payment) is given by `capacity_msat`. It may be determined from various sources
+       /// the same payment) is given by `capacity`. It may be determined from various sources
        /// 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, send_amt_msat: u64, capacity: EffectiveCapacity, source: &NodeId, target: &NodeId) -> 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 +100,8 @@ pub trait Score $(: $supertrait)* {
 }
 
 impl<S: Score, T: DerefMut<Target=S> $(+ $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, send_amt_msat: u64, capacity: EffectiveCapacity, source: &NodeId, target: &NodeId) -> u64 {
+               self.deref().channel_penalty_msat(short_channel_id, send_amt_msat, capacity, source, target)
        }
 
        fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
@@ -209,7 +209,7 @@ impl FixedPenaltyScorer {
 }
 
 impl Score for FixedPenaltyScorer {
-       fn channel_penalty_msat(&self, _: u64, _: u64, _: u64, _: &NodeId, _: &NodeId) -> u64 {
+       fn channel_penalty_msat(&self, _: u64, _: u64, _: EffectiveCapacity, _: &NodeId, _: &NodeId) -> u64 {
                self.penalty_msat
        }
 
@@ -382,19 +382,21 @@ impl Default for ScoringParameters {
 
 impl<T: Time> Score for ScorerUsingTime<T> {
        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, send_amt_msat: u64, capacity: EffectiveCapacity, _source: &NodeId, _target: &NodeId
        ) -> 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_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(
-                                       (send_1024ths - self.params.overuse_penalty_start_1024th as u64)
-                                       .checked_mul(self.params.overuse_penalty_msat_per_1024th).unwrap_or(u64::max_value()))
-                               .unwrap_or(u64::max_value());
+               if let Some(capacity_msat) = capacity.as_msat_with_default() {
+                       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(
+                                               (send_1024ths - self.params.overuse_penalty_start_1024th as u64)
+                                               .checked_mul(self.params.overuse_penalty_msat_per_1024th).unwrap_or(u64::max_value()))
+                                       .unwrap_or(u64::max_value());
+                       }
                }
 
                penalty_msat
@@ -734,17 +736,20 @@ impl<L: DerefMut<Target = u64>, T: Time, U: DerefMut<Target = T>> DirectedChanne
 }
 
 impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsingTime<G, T> {
+       #[inline]
        fn channel_penalty_msat(
-               &self, short_channel_id: u64, amount_msat: u64, capacity_msat: u64, source: &NodeId,
-               target: &NodeId
+               &self, short_channel_id: u64, amount_msat: u64, capacity: EffectiveCapacity,
+               source: &NodeId, target: &NodeId
        ) -> u64 {
                let liquidity_penalty_multiplier_msat = self.params.liquidity_penalty_multiplier_msat;
                let liquidity_offset_half_life = self.params.liquidity_offset_half_life;
-               self.channel_liquidities
-                       .get(&short_channel_id)
-                       .unwrap_or(&ChannelLiquidity::new())
-                       .as_directed(source, target, capacity_msat, liquidity_offset_half_life)
-                       .penalty_msat(amount_msat, liquidity_penalty_multiplier_msat)
+               if let Some(capacity_msat) = capacity.as_msat_with_default() {
+                       self.channel_liquidities
+                               .get(&short_channel_id)
+                               .unwrap_or(&ChannelLiquidity::new())
+                               .as_directed(source, target, capacity_msat, liquidity_offset_half_life)
+                               .penalty_msat(amount_msat, liquidity_penalty_multiplier_msat)
+               } else { 0 }
        }
 
        fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
@@ -759,21 +764,22 @@ impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsin
 
                        // Only score announced channels.
                        if let Some((channel, source)) = channel_directed_from_source {
-                               let capacity_msat = channel.effective_capacity().as_msat();
-                               if hop.short_channel_id == short_channel_id {
+                               if let Some(capacity_msat) = channel.effective_capacity().as_msat_with_default() {
+                                       if hop.short_channel_id == short_channel_id {
+                                               self.channel_liquidities
+                                                       .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);
+                                               break;
+                                       }
+
                                        self.channel_liquidities
                                                .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);
-                                       break;
+                                               .failed_downstream(amount_msat);
                                }
-
-                               self.channel_liquidities
-                                       .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);
                        }
                }
        }
@@ -790,12 +796,13 @@ impl<G: Deref<Target = NetworkGraph>, T: Time> Score for ProbabilisticScorerUsin
 
                        // Only score announced channels.
                        if let Some((channel, source)) = channel_directed_from_source {
-                               let capacity_msat = channel.effective_capacity().as_msat();
-                               self.channel_liquidities
-                                       .entry(hop.short_channel_id)
-                                       .or_insert_with(ChannelLiquidity::new)
-                                       .as_directed_mut(source, &target, capacity_msat, liquidity_offset_half_life)
-                                       .successful(amount_msat);
+                               if let Some(capacity_msat) = channel.effective_capacity().as_msat_with_default() {
+                                       self.channel_liquidities
+                                               .entry(hop.short_channel_id)
+                                               .or_insert_with(ChannelLiquidity::new)
+                                               .as_directed_mut(source, &target, capacity_msat, liquidity_offset_half_life)
+                                               .successful(amount_msat);
+                               }
                        }
                }
        }
@@ -1059,7 +1066,7 @@ mod tests {
        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::network_graph::{EffectiveCapacity, NetworkGraph, NodeId};
        use routing::router::RouteHop;
        use util::ser::{Readable, ReadableArgs, Writeable};
 
@@ -1182,10 +1189,10 @@ 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);
+               assert_eq!(scorer.channel_penalty_msat(42, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_000);
        }
 
        #[test]
@@ -1199,16 +1206,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);
+               assert_eq!(scorer.channel_penalty_msat(42, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_192);
        }
 
        #[test]
@@ -1222,25 +1229,25 @@ 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);
+               assert_eq!(scorer.channel_penalty_msat(42, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_000);
        }
 
        #[test]
@@ -1254,18 +1261,18 @@ 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);
+               assert_eq!(scorer.channel_penalty_msat(42, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_000);
        }
 
        #[test]
@@ -1279,19 +1286,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);
+               assert_eq!(scorer.channel_penalty_msat(42, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_384);
        }
 
        #[test]
@@ -1305,13 +1312,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);
+               assert_eq!(scorer.channel_penalty_msat(42, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_256);
 
                let hop = RouteHop {
                        pubkey: PublicKey::from_slice(target.as_slice()).unwrap(),
@@ -1322,10 +1329,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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_064);
        }
 
        #[test]
@@ -1341,20 +1348,20 @@ mod tests {
                let target = target_node_id();
 
                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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_512);
 
                let mut serialized_scorer = Vec::new();
                scorer.write(&mut serialized_scorer).unwrap();
 
                let deserialized_scorer = <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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_256);
+               assert_eq!(deserialized_scorer.channel_penalty_msat(43, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_512);
        }
 
        #[test]
@@ -1370,7 +1377,7 @@ mod tests {
                let target = target_node_id();
 
                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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_512);
 
                let mut serialized_scorer = Vec::new();
                scorer.write(&mut serialized_scorer).unwrap();
@@ -1378,10 +1385,10 @@ mod tests {
                SinceEpoch::advance(Duration::from_secs(10));
 
                let deserialized_scorer = <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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 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, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1 }, &source, &target), 1_128);
        }
 
        #[test]
@@ -1396,11 +1403,11 @@ 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);
+               assert_eq!(scorer.channel_penalty_msat(42, 1_000, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024_000 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 256_999, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024_000 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 257_000, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024_000 }, &source, &target), 100);
+               assert_eq!(scorer.channel_penalty_msat(42, 258_000, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024_000 }, &source, &target), 200);
+               assert_eq!(scorer.channel_penalty_msat(42, 512_000, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024_000 }, &source, &target), 256 * 100);
        }
 
        // `ProbabilisticScorer` tests
@@ -1728,18 +1735,18 @@ mod tests {
                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), 14);
-               assert_eq!(scorer.channel_penalty_msat(42, 102_400, 1_024_000, &source, &target), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, 1_024_000, 1_024_000, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 1_024, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024_000 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 10_240, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024_000 }, &source, &target), 14);
+               assert_eq!(scorer.channel_penalty_msat(42, 102_400, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024_000 }, &source, &target), 43);
+               assert_eq!(scorer.channel_penalty_msat(42, 1_024_000, EffectiveCapacity::ExactLiquidity { liquidity_msat: 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), 204);
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 301);
-               assert_eq!(scorer.channel_penalty_msat(42, 640, 1_024, &source, &target), 426);
-               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), 903);
+               assert_eq!(scorer.channel_penalty_msat(42, 128, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 58);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 125);
+               assert_eq!(scorer.channel_penalty_msat(42, 374, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 204);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 301);
+               assert_eq!(scorer.channel_penalty_msat(42, 640, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 426);
+               assert_eq!(scorer.channel_penalty_msat(42, 768, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 602);
+               assert_eq!(scorer.channel_penalty_msat(42, 896, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 903);
        }
 
        #[test]
@@ -1757,10 +1764,10 @@ 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), 2_000);
-               assert_eq!(scorer.channel_penalty_msat(42, 61, 100, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 39, EffectiveCapacity::ExactLiquidity { liquidity_msat: 100 }, &source, &target), 0);
+               assert_ne!(scorer.channel_penalty_msat(42, 50, EffectiveCapacity::ExactLiquidity { liquidity_msat: 100 }, &source, &target), 0);
+               assert_ne!(scorer.channel_penalty_msat(42, 50, EffectiveCapacity::ExactLiquidity { liquidity_msat: 100 }, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 61, EffectiveCapacity::ExactLiquidity { liquidity_msat: 100 }, &source, &target), 2_000);
        }
 
        #[test]
@@ -1775,13 +1782,13 @@ mod tests {
                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), 300);
+               assert_eq!(scorer.channel_penalty_msat(41, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &sender, &source), 300);
 
                scorer.payment_path_failed(&failed_path.iter().collect::<Vec<_>>(), 41);
-               assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 300);
+               assert_eq!(scorer.channel_penalty_msat(41, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &sender, &source), 300);
 
                scorer.payment_path_successful(&successful_path.iter().collect::<Vec<_>>());
-               assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 300);
+               assert_eq!(scorer.channel_penalty_msat(41, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &sender, &source), 300);
        }
 
        #[test]
@@ -1795,15 +1802,15 @@ mod tests {
                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), 300);
-               assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 601);
+               assert_eq!(scorer.channel_penalty_msat(42, 250, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 128);
+               assert_eq!(scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, 750, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 601);
 
                scorer.payment_path_failed(&path.iter().collect::<Vec<_>>(), 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);
+               assert_eq!(scorer.channel_penalty_msat(42, 250, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 750, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 300);
        }
 
        #[test]
@@ -1817,15 +1824,15 @@ mod tests {
                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), 300);
-               assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 601);
+               assert_eq!(scorer.channel_penalty_msat(42, 250, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 128);
+               assert_eq!(scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, 750, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 601);
 
                scorer.payment_path_failed(&path.iter().collect::<Vec<_>>(), 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), 2_000);
-               assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 250, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 750, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 2_000);
        }
 
        #[test]
@@ -1841,15 +1848,15 @@ mod tests {
                let recipient = recipient_node_id();
                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, 250, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &sender, &source), 128);
+               assert_eq!(scorer.channel_penalty_msat(42, 250, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 128);
+               assert_eq!(scorer.channel_penalty_msat(43, 250, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &target, &recipient), 128);
 
                scorer.payment_path_successful(&path.iter().collect::<Vec<_>>());
 
-               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, 250, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &sender, &source), 128);
+               assert_eq!(scorer.channel_penalty_msat(42, 250, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 300);
+               assert_eq!(scorer.channel_penalty_msat(43, 250, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &target, &recipient), 300);
        }
 
        #[test]
@@ -1863,44 +1870,44 @@ mod tests {
                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);
+               assert_eq!(scorer.channel_penalty_msat(42, 0, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 1_024, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 2_000);
 
                scorer.payment_path_failed(&payment_path_for_amount(768).iter().collect::<Vec<_>>(), 42);
                scorer.payment_path_failed(&payment_path_for_amount(128).iter().collect::<Vec<_>>(), 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), 97);
-               assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_409);
-               assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 128, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 97);
+               assert_eq!(scorer.channel_penalty_msat(42, 768, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 1_409);
+               assert_eq!(scorer.channel_penalty_msat(42, 896, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 2_000);
 
                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), 97);
-               assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_409);
-               assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 128, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 97);
+               assert_eq!(scorer.channel_penalty_msat(42, 768, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 1_409);
+               assert_eq!(scorer.channel_penalty_msat(42, 896, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 2_000);
 
                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_773);
-               assert_eq!(scorer.channel_penalty_msat(42, 960, 1_024, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 64, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 128, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 34);
+               assert_eq!(scorer.channel_penalty_msat(42, 896, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 1_773);
+               assert_eq!(scorer.channel_penalty_msat(42, 960, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 2_000);
 
                // 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);
+               assert_eq!(scorer.channel_penalty_msat(42, 0, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 1, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 1_023, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 1_024, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 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);
+               assert_eq!(scorer.channel_penalty_msat(42, 0, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 1_024, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 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);
+               assert_eq!(scorer.channel_penalty_msat(42, 0, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, 1_024, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 2_000);
        }
 
        #[test]
@@ -1913,18 +1920,18 @@ mod tests {
                let mut scorer = ProbabilisticScorer::new(params, &network_graph);
                let source = source_node_id();
                let target = target_node_id();
-               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 125);
 
                scorer.payment_path_failed(&payment_path_for_amount(512).iter().collect::<Vec<_>>(), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 274);
+               assert_eq!(scorer.channel_penalty_msat(42, 256, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 274);
 
                // 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, 256, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 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, 256, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 125);
        }
 
        #[test]
@@ -1938,30 +1945,30 @@ mod tests {
                let source = source_node_id();
                let target = target_node_id();
 
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 301);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 301);
 
                // More knowledge gives higher confidence (256, 768), meaning a lower penalty.
                scorer.payment_path_failed(&payment_path_for_amount(768).iter().collect::<Vec<_>>(), 42);
                scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::<Vec<_>>(), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 274);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 274);
 
                // 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), 301);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 301);
 
                // 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::<Vec<_>>());
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 342);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 342);
 
                // 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::<Vec<_>>(), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 255);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 255);
 
                // 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), 284);
+               assert_eq!(scorer.channel_penalty_msat(42, 512, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_024 }, &source, &target), 284);
        }
 
        #[test]
@@ -1976,13 +1983,13 @@ mod tests {
                let target = target_node_id();
 
                scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::<Vec<_>>(), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 2_000);
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 472);
+               assert_eq!(scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 472);
 
                scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::<Vec<_>>(), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 300);
 
                let mut serialized_scorer = Vec::new();
                scorer.write(&mut serialized_scorer).unwrap();
@@ -1990,7 +1997,7 @@ mod tests {
                let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
                let deserialized_scorer =
                        <ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph)).unwrap();
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
+               assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 300);
        }
 
        #[test]
@@ -2005,7 +2012,7 @@ mod tests {
                let target = target_node_id();
 
                scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::<Vec<_>>(), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 2_000);
 
                let mut serialized_scorer = Vec::new();
                scorer.write(&mut serialized_scorer).unwrap();
@@ -2015,12 +2022,12 @@ mod tests {
                let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
                let deserialized_scorer =
                        <ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph)).unwrap();
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 472);
+               assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 472);
 
                scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::<Vec<_>>(), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 300);
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 371);
+               assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 }, &source, &target), 371);
        }
 }