Add a ChaCha20 utility for encrypting in place
[rust-lightning] / lightning / src / routing / scoring.rs
index e664b0d11e7c843b71cac7bd781c9711fd00fb59..75dae72248b13f0100a8b350459580a2957109e5 100644 (file)
@@ -19,7 +19,7 @@
 //! #
 //! # use lightning::routing::gossip::NetworkGraph;
 //! # use lightning::routing::router::{RouteParameters, find_route};
-//! # use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters};
+//! # use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters};
 //! # use lightning::sign::KeysManager;
 //! # use lightning::util::logger::{Logger, Record};
 //! # use bitcoin::secp256k1::PublicKey;
 //! # let logger = FakeLogger {};
 //! #
 //! // Use the default channel penalties.
-//! let params = ProbabilisticScoringParameters::default();
-//! let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+//! let params = ProbabilisticScoringFeeParameters::default();
+//! let decay_params = ProbabilisticScoringDecayParameters::default();
+//! let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
 //!
 //! // Or use custom channel penalties.
-//! let params = ProbabilisticScoringParameters {
-//!     liquidity_penalty_multiplier_msat: 2 * 1000,
-//!     ..ProbabilisticScoringParameters::default()
+//! let params = ProbabilisticScoringFeeParameters {
+//!    liquidity_penalty_multiplier_msat: 2 * 1000,
+//!    ..ProbabilisticScoringFeeParameters::default()
 //! };
-//! let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+//! let decay_params = ProbabilisticScoringDecayParameters::default();
+//! let scorer = ProbabilisticScorer::new(decay_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);
+//! let route = find_route(&payer, &route_params, &network_graph, None, &logger, &scorer, &params, &random_seed_bytes);
 //! # }
 //! ```
 //!
@@ -86,6 +88,10 @@ macro_rules! define_score { ($($supertrait: path)*) => {
 ///
 ///    Scoring is in terms of fees willing to be paid in order to avoid routing through a channel.
 pub trait Score $(: $supertrait)* {
+       /// A configurable type which should contain various passed-in parameters for configuring the scorer,
+       /// on a per-routefinding-call basis through to the scorer methods,
+       /// which are used to determine the parameters for the suitability of channels for use.
+       type ScoreParams;
        /// Returns the fee in msats willing to be paid to avoid routing `send_amt_msat` through the
        /// given channel in the direction from `source` to `target`.
        ///
@@ -95,7 +101,7 @@ pub trait Score $(: $supertrait)* {
        /// [`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, source: &NodeId, target: &NodeId, usage: ChannelUsage
+               &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams
        ) -> u64;
 
        /// Handles updating channel penalties after failing to route through a channel.
@@ -112,10 +118,11 @@ pub trait Score $(: $supertrait)* {
 }
 
 impl<S: Score, T: DerefMut<Target=S> $(+ $supertrait)*> Score for T {
+       type ScoreParams = S::ScoreParams;
        fn channel_penalty_msat(
-               &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage
+               &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams
        ) -> u64 {
-               self.deref().channel_penalty_msat(short_channel_id, source, target, usage)
+               self.deref().channel_penalty_msat(short_channel_id, source, target, usage, score_params)
        }
 
        fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) {
@@ -150,8 +157,11 @@ define_score!();
 ///
 /// [`find_route`]: crate::routing::router::find_route
 pub trait LockableScore<'a> {
+       /// The [`Score`] type.
+       type Score: 'a + Score;
+
        /// The locked [`Score`] type.
-       type Locked: 'a + Score;
+       type Locked: DerefMut<Target = Self::Score> + Sized;
 
        /// Returns the locked scorer.
        fn lock(&'a self) -> Self::Locked;
@@ -165,61 +175,38 @@ pub trait WriteableScore<'a>: LockableScore<'a> + Writeable {}
 
 #[cfg(not(c_bindings))]
 impl<'a, T> WriteableScore<'a> for T where T: LockableScore<'a> + Writeable {}
-/// This is not exported to bindings users
+#[cfg(not(c_bindings))]
 impl<'a, T: 'a + Score> LockableScore<'a> for Mutex<T> {
+       type Score = T;
        type Locked = MutexGuard<'a, T>;
 
-       fn lock(&'a self) -> MutexGuard<'a, T> {
+       fn lock(&'a self) -> Self::Locked {
                Mutex::lock(self).unwrap()
        }
 }
 
+#[cfg(not(c_bindings))]
 impl<'a, T: 'a + Score> LockableScore<'a> for RefCell<T> {
+       type Score = T;
        type Locked = RefMut<'a, T>;
 
-       fn lock(&'a self) -> RefMut<'a, T> {
+       fn lock(&'a self) -> Self::Locked {
                self.borrow_mut()
        }
 }
 
 #[cfg(c_bindings)]
 /// A concrete implementation of [`LockableScore`] which supports multi-threading.
-pub struct MultiThreadedLockableScore<S: Score> {
-       score: Mutex<S>,
-}
-#[cfg(c_bindings)]
-/// A locked `MultiThreadedLockableScore`.
-pub struct MultiThreadedScoreLock<'a, S: Score>(MutexGuard<'a, S>);
-#[cfg(c_bindings)]
-impl<'a, T: Score + 'a> Score for MultiThreadedScoreLock<'a, T> {
-       fn channel_penalty_msat(&self, scid: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage) -> u64 {
-               self.0.channel_penalty_msat(scid, source, target, usage)
-       }
-       fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) {
-               self.0.payment_path_failed(path, short_channel_id)
-       }
-       fn payment_path_successful(&mut self, path: &Path) {
-               self.0.payment_path_successful(path)
-       }
-       fn probe_failed(&mut self, path: &Path, short_channel_id: u64) {
-               self.0.probe_failed(path, short_channel_id)
-       }
-       fn probe_successful(&mut self, path: &Path) {
-               self.0.probe_successful(path)
-       }
-}
-#[cfg(c_bindings)]
-impl<'a, T: Score + 'a> Writeable for MultiThreadedScoreLock<'a, T> {
-       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
-               self.0.write(writer)
-       }
+pub struct MultiThreadedLockableScore<T: Score> {
+       score: Mutex<T>,
 }
 
 #[cfg(c_bindings)]
-impl<'a, T: Score + 'a> LockableScore<'a> for MultiThreadedLockableScore<T> {
+impl<'a, T: 'a + Score> LockableScore<'a> for MultiThreadedLockableScore<T> {
+       type Score = T;
        type Locked = MultiThreadedScoreLock<'a, T>;
 
-       fn lock(&'a self) -> MultiThreadedScoreLock<'a, T> {
+       fn lock(&'a self) -> Self::Locked {
                MultiThreadedScoreLock(Mutex::lock(&self.score).unwrap())
        }
 }
@@ -232,7 +219,7 @@ impl<T: Score> Writeable for MultiThreadedLockableScore<T> {
 }
 
 #[cfg(c_bindings)]
-impl<'a, T: Score + 'a> WriteableScore<'a> for MultiThreadedLockableScore<T> {}
+impl<'a, T: 'a + Score> WriteableScore<'a> for MultiThreadedLockableScore<T> {}
 
 #[cfg(c_bindings)]
 impl<T: Score> MultiThreadedLockableScore<T> {
@@ -243,21 +230,34 @@ impl<T: Score> MultiThreadedLockableScore<T> {
 }
 
 #[cfg(c_bindings)]
-/// This is not exported to bindings users
-impl<'a, T: Writeable> Writeable for RefMut<'a, T> {
+/// A locked `MultiThreadedLockableScore`.
+pub struct MultiThreadedScoreLock<'a, T: Score>(MutexGuard<'a, T>);
+
+#[cfg(c_bindings)]
+impl<'a, T: 'a + Score> Writeable for MultiThreadedScoreLock<'a, T> {
        fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
-               T::write(&**self, writer)
+               self.0.write(writer)
        }
 }
 
 #[cfg(c_bindings)]
-/// This is not exported to bindings users
-impl<'a, S: Writeable> Writeable for MutexGuard<'a, S> {
-       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
-               S::write(&**self, writer)
-       }
+impl<'a, T: 'a + Score> DerefMut for MultiThreadedScoreLock<'a, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.0.deref_mut()
+    }
+}
+
+#[cfg(c_bindings)]
+impl<'a, T: 'a + Score> Deref for MultiThreadedScoreLock<'a, T> {
+       type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        self.0.deref()
+    }
 }
 
+
+
 /// Proposed use of a channel passed as a parameter to [`Score::channel_penalty_msat`].
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub struct ChannelUsage {
@@ -286,7 +286,8 @@ impl FixedPenaltyScorer {
 }
 
 impl Score for FixedPenaltyScorer {
-       fn channel_penalty_msat(&self, _: u64, _: &NodeId, _: &NodeId, _: ChannelUsage) -> u64 {
+       type ScoreParams = ();
+       fn channel_penalty_msat(&self, _: u64, _: &NodeId, _: &NodeId, _: ChannelUsage, _score_params: &Self::ScoreParams) -> u64 {
                self.penalty_msat
        }
 
@@ -316,7 +317,7 @@ impl ReadableArgs<u64> for FixedPenaltyScorer {
 }
 
 #[cfg(not(feature = "no-std"))]
-type ConfiguredTime = std::time::Instant;
+type ConfiguredTime = crate::util::time::MonotonicTime;
 #[cfg(feature = "no-std")]
 use crate::util::time::Eternity;
 #[cfg(feature = "no-std")]
@@ -353,11 +354,11 @@ type ConfiguredTime = Eternity;
 /// behavior.
 ///
 /// [1]: https://arxiv.org/abs/2107.05322
-/// [`liquidity_penalty_multiplier_msat`]: ProbabilisticScoringParameters::liquidity_penalty_multiplier_msat
-/// [`liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringParameters::liquidity_penalty_amount_multiplier_msat
-/// [`liquidity_offset_half_life`]: ProbabilisticScoringParameters::liquidity_offset_half_life
-/// [`historical_liquidity_penalty_multiplier_msat`]: ProbabilisticScoringParameters::historical_liquidity_penalty_multiplier_msat
-/// [`historical_liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringParameters::historical_liquidity_penalty_amount_multiplier_msat
+/// [`liquidity_penalty_multiplier_msat`]: ProbabilisticScoringFeeParameters::liquidity_penalty_multiplier_msat
+/// [`liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringFeeParameters::liquidity_penalty_amount_multiplier_msat
+/// [`liquidity_offset_half_life`]: ProbabilisticScoringDecayParameters::liquidity_offset_half_life
+/// [`historical_liquidity_penalty_multiplier_msat`]: ProbabilisticScoringFeeParameters::historical_liquidity_penalty_multiplier_msat
+/// [`historical_liquidity_penalty_amount_multiplier_msat`]: ProbabilisticScoringFeeParameters::historical_liquidity_penalty_amount_multiplier_msat
 pub type ProbabilisticScorer<G, L> = ProbabilisticScorerUsingTime::<G, L, ConfiguredTime>;
 
 /// Probabilistic [`Score`] implementation.
@@ -365,7 +366,7 @@ pub type ProbabilisticScorer<G, L> = ProbabilisticScorerUsingTime::<G, L, Config
 /// This is not exported to bindings users generally all users should use the [`ProbabilisticScorer`] type alias.
 pub struct ProbabilisticScorerUsingTime<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time>
 where L::Target: Logger {
-       params: ProbabilisticScoringParameters,
+       decay_params: ProbabilisticScoringDecayParameters,
        network_graph: G,
        logger: L,
        // TODO: Remove entries of closed channels.
@@ -380,7 +381,7 @@ where L::Target: Logger {
 /// The penalty applied to any channel by the [`ProbabilisticScorer`] is the sum of each of the
 /// parameters here.
 #[derive(Clone)]
-pub struct ProbabilisticScoringParameters {
+pub struct ProbabilisticScoringFeeParameters {
        /// A fixed penalty in msats to apply to each channel.
        ///
        /// Default value: 500 msat
@@ -415,30 +416,9 @@ pub struct ProbabilisticScoringParameters {
        ///
        /// Default value: 30,000 msat
        ///
-       /// [`liquidity_offset_half_life`]: Self::liquidity_offset_half_life
+       /// [`liquidity_offset_half_life`]: ProbabilisticScoringDecayParameters::liquidity_offset_half_life
        pub liquidity_penalty_multiplier_msat: u64,
 
-       /// Whenever this amount of time elapses since the last update to a channel's liquidity bounds,
-       /// the distance from the bounds to "zero" is cut in half. In other words, the lower-bound on
-       /// the available liquidity is halved and the upper-bound moves half-way to the channel's total
-       /// capacity.
-       ///
-       /// Because halving the liquidity bounds grows the uncertainty on the channel's liquidity,
-       /// the penalty for an amount within the new bounds may change. See the [`ProbabilisticScorer`]
-       /// struct documentation for more info on the way the liquidity bounds are used.
-       ///
-       /// For example, if the channel's capacity is 1 million sats, and the current upper and lower
-       /// liquidity bounds are 200,000 sats and 600,000 sats, after this amount of time the upper
-       /// and lower liquidity bounds will be decayed to 100,000 and 800,000 sats.
-       ///
-       /// Default value: 6 hours
-       ///
-       /// # Note
-       ///
-       /// When built with the `no-std` feature, time will never elapse. Therefore, the channel
-       /// liquidity knowledge will never decay except when the bounds cross.
-       pub liquidity_offset_half_life: Duration,
-
        /// A multiplier used in conjunction with a payment amount and the negative `log10` of the
        /// channel's success probability for the payment, as determined by our latest estimates of the
        /// channel's liquidity, to determine the amount penalty.
@@ -495,16 +475,6 @@ pub struct ProbabilisticScoringParameters {
        /// [`liquidity_penalty_amount_multiplier_msat`]: Self::liquidity_penalty_amount_multiplier_msat
        pub historical_liquidity_penalty_amount_multiplier_msat: u64,
 
-       /// If we aren't learning any new datapoints for a channel, the historical liquidity bounds
-       /// tracking can simply live on with increasingly stale data. Instead, when a channel has not
-       /// seen a liquidity estimate update for this amount of time, the historical datapoints are
-       /// decayed by half.
-       ///
-       /// Note that after 16 or more half lives all historical data will be completely gone.
-       ///
-       /// Default value: 14 days
-       pub historical_no_updates_half_life: Duration,
-
        /// Manual penalties used for the given nodes. Allows to set a particular penalty for a given
        /// node. Note that a manual penalty of `u64::max_value()` means the node would not ever be
        /// considered during path finding.
@@ -513,9 +483,10 @@ pub struct ProbabilisticScoringParameters {
        pub manual_node_penalties: HashMap<NodeId, u64>,
 
        /// This penalty is applied when `htlc_maximum_msat` is equal to or larger than half of the
-       /// channel's capacity, which makes us prefer nodes with a smaller `htlc_maximum_msat`. We
-       /// treat such nodes preferentially as this makes balance discovery attacks harder to execute,
-       /// thereby creating an incentive to restrict `htlc_maximum_msat` and improve privacy.
+       /// channel's capacity, (ie. htlc_maximum_msat >= 0.5 * channel_capacity) which makes us
+       /// prefer nodes with a smaller `htlc_maximum_msat`. We treat such nodes preferentially
+       /// as this makes balance discovery attacks harder to execute, thereby creating an incentive
+       /// to restrict `htlc_maximum_msat` and improve privacy.
        ///
        /// Default value: 250 msat
        pub anti_probing_penalty_msat: u64,
@@ -540,6 +511,136 @@ pub struct ProbabilisticScoringParameters {
        pub considered_impossible_penalty_msat: u64,
 }
 
+impl Default for ProbabilisticScoringFeeParameters {
+       fn default() -> Self {
+               Self {
+                       base_penalty_msat: 500,
+                       base_penalty_amount_multiplier_msat: 8192,
+                       liquidity_penalty_multiplier_msat: 30_000,
+                       liquidity_penalty_amount_multiplier_msat: 192,
+                       manual_node_penalties: HashMap::new(),
+                       anti_probing_penalty_msat: 250,
+                       considered_impossible_penalty_msat: 1_0000_0000_000,
+                       historical_liquidity_penalty_multiplier_msat: 10_000,
+                       historical_liquidity_penalty_amount_multiplier_msat: 64,
+               }
+       }
+}
+
+impl ProbabilisticScoringFeeParameters {
+       /// Marks the node with the given `node_id` as banned,
+       /// i.e it will be avoided during path finding.
+       pub fn add_banned(&mut self, node_id: &NodeId) {
+               self.manual_node_penalties.insert(*node_id, u64::max_value());
+       }
+
+       /// Marks all nodes in the given list as banned, i.e.,
+       /// they will be avoided during path finding.
+       pub fn add_banned_from_list(&mut self, node_ids: Vec<NodeId>) {
+               for id in node_ids {
+                       self.manual_node_penalties.insert(id, u64::max_value());
+               }
+       }
+
+       /// Removes the node with the given `node_id` from the list of nodes to avoid.
+       pub fn remove_banned(&mut self, node_id: &NodeId) {
+               self.manual_node_penalties.remove(node_id);
+       }
+
+       /// Sets a manual penalty for the given node.
+       pub fn set_manual_penalty(&mut self, node_id: &NodeId, penalty: u64) {
+               self.manual_node_penalties.insert(*node_id, penalty);
+       }
+
+       /// Removes the node with the given `node_id` from the list of manual penalties.
+       pub fn remove_manual_penalty(&mut self, node_id: &NodeId) {
+               self.manual_node_penalties.remove(node_id);
+       }
+
+       /// Clears the list of manual penalties that are applied during path finding.
+       pub fn clear_manual_penalties(&mut self) {
+               self.manual_node_penalties = HashMap::new();
+       }
+}
+
+#[cfg(test)]
+impl ProbabilisticScoringFeeParameters {
+       fn zero_penalty() -> Self {
+               Self {
+                       base_penalty_msat: 0,
+                       base_penalty_amount_multiplier_msat: 0,
+                       liquidity_penalty_multiplier_msat: 0,
+                       liquidity_penalty_amount_multiplier_msat: 0,
+                       historical_liquidity_penalty_multiplier_msat: 0,
+                       historical_liquidity_penalty_amount_multiplier_msat: 0,
+                       manual_node_penalties: HashMap::new(),
+                       anti_probing_penalty_msat: 0,
+                       considered_impossible_penalty_msat: 0,
+               }
+       }
+}
+
+/// Parameters for configuring [`ProbabilisticScorer`].
+///
+/// Used to configure decay parameters that are static throughout the lifetime of the scorer.
+/// these decay parameters affect the score of the channel penalty and are not changed on a
+/// per-route penalty cost call.
+#[derive(Copy, Clone)]
+pub struct ProbabilisticScoringDecayParameters {
+       /// If we aren't learning any new datapoints for a channel, the historical liquidity bounds
+       /// tracking can simply live on with increasingly stale data. Instead, when a channel has not
+       /// seen a liquidity estimate update for this amount of time, the historical datapoints are
+       /// decayed by half.
+       /// For an example of historical_no_updates_half_life being used see [`historical_estimated_channel_liquidity_probabilities`]
+       ///
+       /// Note that after 16 or more half lives all historical data will be completely gone.
+       ///
+       /// Default value: 14 days
+       ///
+       /// [`historical_estimated_channel_liquidity_probabilities`]: ProbabilisticScorerUsingTime::historical_estimated_channel_liquidity_probabilities
+       pub historical_no_updates_half_life: Duration,
+
+       /// Whenever this amount of time elapses since the last update to a channel's liquidity bounds,
+       /// the distance from the bounds to "zero" is cut in half. In other words, the lower-bound on
+       /// the available liquidity is halved and the upper-bound moves half-way to the channel's total
+       /// capacity.
+       ///
+       /// Because halving the liquidity bounds grows the uncertainty on the channel's liquidity,
+       /// the penalty for an amount within the new bounds may change. See the [`ProbabilisticScorer`]
+       /// struct documentation for more info on the way the liquidity bounds are used.
+       ///
+       /// For example, if the channel's capacity is 1 million sats, and the current upper and lower
+       /// liquidity bounds are 200,000 sats and 600,000 sats, after this amount of time the upper
+       /// and lower liquidity bounds will be decayed to 100,000 and 800,000 sats.
+       ///
+       /// Default value: 6 hours
+       ///
+       /// # Note
+       ///
+       /// When built with the `no-std` feature, time will never elapse. Therefore, the channel
+       /// liquidity knowledge will never decay except when the bounds cross.
+       pub liquidity_offset_half_life: Duration,
+}
+
+impl Default for ProbabilisticScoringDecayParameters {
+       fn default() -> Self {
+               Self {
+                       liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
+                       historical_no_updates_half_life: Duration::from_secs(60 * 60 * 24 * 14),
+               }
+       }
+}
+
+#[cfg(test)]
+impl ProbabilisticScoringDecayParameters {
+       fn zero_penalty() -> Self {
+               Self {
+                       liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
+                       historical_no_updates_half_life: Duration::from_secs(60 * 60 * 24 * 14),
+               }
+       }
+}
+
 /// Tracks the historical state of a distribution as a weighted average of how much time was spent
 /// in each of 8 buckets.
 #[derive(Clone, Copy)]
@@ -702,7 +803,7 @@ struct ChannelLiquidity<T: Time> {
 
 /// A snapshot of [`ChannelLiquidity`] in one direction assuming a certain channel capacity and
 /// decayed with a given half life.
-struct DirectedChannelLiquidity<'a, L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Time, U: Deref<Target = T>> {
+struct DirectedChannelLiquidity<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Time, U: Deref<Target = T>> {
        min_liquidity_offset_msat: L,
        max_liquidity_offset_msat: L,
        min_liquidity_offset_history: BRT,
@@ -711,15 +812,15 @@ struct DirectedChannelLiquidity<'a, L: Deref<Target = u64>, BRT: Deref<Target =
        capacity_msat: u64,
        last_updated: U,
        now: T,
-       params: &'a ProbabilisticScoringParameters,
+       decay_params: ProbabilisticScoringDecayParameters,
 }
 
 impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerUsingTime<G, L, T> 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, logger: L) -> Self {
+       pub fn new(decay_params: ProbabilisticScoringDecayParameters, network_graph: G, logger: L) -> Self {
                Self {
-                       params,
+                       decay_params,
                        network_graph,
                        logger,
                        channel_liquidities: HashMap::new(),
@@ -745,14 +846,14 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
                                let log_direction = |source, target| {
                                        if let Some((directed_info, _)) = chan_debug.as_directed_to(target) {
                                                let amt = directed_info.effective_capacity().as_msat();
-                                               let dir_liq = liq.as_directed(source, target, 0, amt, &self.params);
+                                               let dir_liq = liq.as_directed(source, target, 0, amt, self.decay_params);
 
                                                let buckets = HistoricalMinMaxBuckets {
                                                        min_liquidity_offset_history: &dir_liq.min_liquidity_offset_history,
                                                        max_liquidity_offset_history: &dir_liq.max_liquidity_offset_history,
                                                };
                                                let (min_buckets, max_buckets, _) = buckets.get_decayed_buckets(now,
-                                                       *dir_liq.last_updated, self.params.historical_no_updates_half_life);
+                                                       *dir_liq.last_updated, self.decay_params.historical_no_updates_half_life);
 
                                                log_debug!(self.logger, core::concat!(
                                                        "Liquidity from {} to {} via {} is in the range ({}, {}).\n",
@@ -787,7 +888,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
                        if let Some(liq) = self.channel_liquidities.get(&scid) {
                                if let Some((directed_info, source)) = chan.as_directed_to(target) {
                                        let amt = directed_info.effective_capacity().as_msat();
-                                       let dir_liq = liq.as_directed(source, target, 0, amt, &self.params);
+                                       let dir_liq = liq.as_directed(source, target, 0, amt, self.decay_params);
                                        return Some((dir_liq.min_liquidity_msat(), dir_liq.max_liquidity_msat()));
                                }
                        }
@@ -824,14 +925,14 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
                        if let Some(liq) = self.channel_liquidities.get(&scid) {
                                if let Some((directed_info, source)) = chan.as_directed_to(target) {
                                        let amt = directed_info.effective_capacity().as_msat();
-                                       let dir_liq = liq.as_directed(source, target, 0, amt, &self.params);
+                                       let dir_liq = liq.as_directed(source, target, 0, amt, self.decay_params);
 
                                        let buckets = HistoricalMinMaxBuckets {
                                                min_liquidity_offset_history: &dir_liq.min_liquidity_offset_history,
                                                max_liquidity_offset_history: &dir_liq.max_liquidity_offset_history,
                                        };
                                        let (min_buckets, mut max_buckets, _) = buckets.get_decayed_buckets(T::now(),
-                                               *dir_liq.last_updated, self.params.historical_no_updates_half_life);
+                                               *dir_liq.last_updated, self.decay_params.historical_no_updates_half_life);
                                        // Note that the liquidity buckets are an offset from the edge, so we inverse
                                        // the max order to get the probabilities from zero.
                                        max_buckets.reverse();
@@ -841,77 +942,6 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
                }
                None
        }
-
-       /// Marks the node with the given `node_id` as banned, i.e.,
-       /// it will be avoided during path finding.
-       pub fn add_banned(&mut self, node_id: &NodeId) {
-               self.params.manual_node_penalties.insert(*node_id, u64::max_value());
-       }
-
-       /// Removes the node with the given `node_id` from the list of nodes to avoid.
-       pub fn remove_banned(&mut self, node_id: &NodeId) {
-               self.params.manual_node_penalties.remove(node_id);
-       }
-
-       /// Sets a manual penalty for the given node.
-       pub fn set_manual_penalty(&mut self, node_id: &NodeId, penalty: u64) {
-               self.params.manual_node_penalties.insert(*node_id, penalty);
-       }
-
-       /// Removes the node with the given `node_id` from the list of manual penalties.
-       pub fn remove_manual_penalty(&mut self, node_id: &NodeId) {
-               self.params.manual_node_penalties.remove(node_id);
-       }
-
-       /// Clears the list of manual penalties that are applied during path finding.
-       pub fn clear_manual_penalties(&mut self) {
-               self.params.manual_node_penalties = HashMap::new();
-       }
-}
-
-impl ProbabilisticScoringParameters {
-       #[cfg(test)]
-       fn zero_penalty() -> Self {
-               Self {
-                       base_penalty_msat: 0,
-                       base_penalty_amount_multiplier_msat: 0,
-                       liquidity_penalty_multiplier_msat: 0,
-                       liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
-                       liquidity_penalty_amount_multiplier_msat: 0,
-                       historical_liquidity_penalty_multiplier_msat: 0,
-                       historical_liquidity_penalty_amount_multiplier_msat: 0,
-                       historical_no_updates_half_life: Duration::from_secs(60 * 60 * 24 * 14),
-                       manual_node_penalties: HashMap::new(),
-                       anti_probing_penalty_msat: 0,
-                       considered_impossible_penalty_msat: 0,
-               }
-       }
-
-       /// Marks all nodes in the given list as banned, i.e.,
-       /// they will be avoided during path finding.
-       pub fn add_banned_from_list(&mut self, node_ids: Vec<NodeId>) {
-               for id in node_ids {
-                       self.manual_node_penalties.insert(id, u64::max_value());
-               }
-       }
-}
-
-impl Default for ProbabilisticScoringParameters {
-       fn default() -> Self {
-               Self {
-                       base_penalty_msat: 500,
-                       base_penalty_amount_multiplier_msat: 8192,
-                       liquidity_penalty_multiplier_msat: 30_000,
-                       liquidity_offset_half_life: Duration::from_secs(6 * 60 * 60),
-                       liquidity_penalty_amount_multiplier_msat: 192,
-                       historical_liquidity_penalty_multiplier_msat: 10_000,
-                       historical_liquidity_penalty_amount_multiplier_msat: 64,
-                       historical_no_updates_half_life: Duration::from_secs(60 * 60 * 24 * 14),
-                       manual_node_penalties: HashMap::new(),
-                       anti_probing_penalty_msat: 250,
-                       considered_impossible_penalty_msat: 1_0000_0000_000,
-               }
-       }
 }
 
 impl<T: Time> ChannelLiquidity<T> {
@@ -928,10 +958,9 @@ impl<T: Time> ChannelLiquidity<T> {
 
        /// Returns a view of the channel liquidity directed from `source` to `target` assuming
        /// `capacity_msat`.
-       fn as_directed<'a>(
-               &self, source: &NodeId, target: &NodeId, inflight_htlc_msat: u64, capacity_msat: u64,
-               params: &'a ProbabilisticScoringParameters
-       ) -> DirectedChannelLiquidity<'a, &u64, &HistoricalBucketRangeTracker, T, &T> {
+       fn as_directed(
+               &self, source: &NodeId, target: &NodeId, inflight_htlc_msat: u64, capacity_msat: u64, decay_params: ProbabilisticScoringDecayParameters
+       ) -> DirectedChannelLiquidity<&u64, &HistoricalBucketRangeTracker, T, &T> {
                let (min_liquidity_offset_msat, max_liquidity_offset_msat, min_liquidity_offset_history, max_liquidity_offset_history) =
                        if source < target {
                                (&self.min_liquidity_offset_msat, &self.max_liquidity_offset_msat,
@@ -950,16 +979,15 @@ impl<T: Time> ChannelLiquidity<T> {
                        capacity_msat,
                        last_updated: &self.last_updated,
                        now: T::now(),
-                       params,
+                       decay_params: decay_params,
                }
        }
 
        /// Returns a mutable view of the channel liquidity directed from `source` to `target` assuming
        /// `capacity_msat`.
-       fn as_directed_mut<'a>(
-               &mut self, source: &NodeId, target: &NodeId, inflight_htlc_msat: u64, capacity_msat: u64,
-               params: &'a ProbabilisticScoringParameters
-       ) -> DirectedChannelLiquidity<'a, &mut u64, &mut HistoricalBucketRangeTracker, T, &mut T> {
+       fn as_directed_mut(
+               &mut self, source: &NodeId, target: &NodeId, inflight_htlc_msat: u64, capacity_msat: u64, decay_params: ProbabilisticScoringDecayParameters
+       ) -> DirectedChannelLiquidity<&mut u64, &mut HistoricalBucketRangeTracker, T, &mut T> {
                let (min_liquidity_offset_msat, max_liquidity_offset_msat, min_liquidity_offset_history, max_liquidity_offset_history) =
                        if source < target {
                                (&mut self.min_liquidity_offset_msat, &mut self.max_liquidity_offset_msat,
@@ -978,7 +1006,7 @@ impl<T: Time> ChannelLiquidity<T> {
                        capacity_msat,
                        last_updated: &mut self.last_updated,
                        now: T::now(),
-                       params,
+                       decay_params: decay_params,
                }
        }
 }
@@ -995,10 +1023,10 @@ const PRECISION_LOWER_BOUND_DENOMINATOR: u64 = approx::LOWER_BITS_BOUND;
 const AMOUNT_PENALTY_DIVISOR: u64 = 1 << 20;
 const BASE_AMOUNT_PENALTY_DIVISOR: u64 = 1 << 30;
 
-impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Time, U: Deref<Target = T>> DirectedChannelLiquidity<'_, L, BRT, T, U> {
+impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>, T: Time, U: Deref<Target = T>> DirectedChannelLiquidity< L, BRT, T, U> {
        /// Returns a liquidity penalty for routing the given HTLC `amount_msat` through the channel in
        /// this direction.
-       fn penalty_msat(&self, amount_msat: u64, params: &ProbabilisticScoringParameters) -> u64 {
+       fn penalty_msat(&self, amount_msat: u64, score_params: &ProbabilisticScoringFeeParameters) -> u64 {
                let max_liquidity_msat = self.max_liquidity_msat();
                let min_liquidity_msat = core::cmp::min(self.min_liquidity_msat(), max_liquidity_msat);
 
@@ -1010,9 +1038,9 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
                        // impossibility penalty.
                        let negative_log10_times_2048 = NEGATIVE_LOG10_UPPER_BOUND * 2048;
                        Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
-                                       params.liquidity_penalty_multiplier_msat,
-                                       params.liquidity_penalty_amount_multiplier_msat)
-                               .saturating_add(params.considered_impossible_penalty_msat)
+                                       score_params.liquidity_penalty_multiplier_msat,
+                                       score_params.liquidity_penalty_amount_multiplier_msat)
+                               .saturating_add(score_params.considered_impossible_penalty_msat)
                } else {
                        let numerator = (max_liquidity_msat - amount_msat).saturating_add(1);
                        let denominator = (max_liquidity_msat - min_liquidity_msat).saturating_add(1);
@@ -1025,13 +1053,13 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
                                let negative_log10_times_2048 =
                                        approx::negative_log10_times_2048(numerator, denominator);
                                Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
-                                       params.liquidity_penalty_multiplier_msat,
-                                       params.liquidity_penalty_amount_multiplier_msat)
+                                       score_params.liquidity_penalty_multiplier_msat,
+                                       score_params.liquidity_penalty_amount_multiplier_msat)
                        }
                };
 
-               if params.historical_liquidity_penalty_multiplier_msat != 0 ||
-                  params.historical_liquidity_penalty_amount_multiplier_msat != 0 {
+               if score_params.historical_liquidity_penalty_multiplier_msat != 0 ||
+                  score_params.historical_liquidity_penalty_amount_multiplier_msat != 0 {
                        let payment_amt_64th_bucket = if amount_msat < u64::max_value() / 64 {
                                amount_msat * 64 / self.capacity_msat.saturating_add(1)
                        } else {
@@ -1051,12 +1079,12 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
                        };
                        if let Some(cumulative_success_prob_times_billion) = buckets
                                .calculate_success_probability_times_billion(self.now, *self.last_updated,
-                                       params.historical_no_updates_half_life, payment_amt_64th_bucket as u8)
+                                       self.decay_params.historical_no_updates_half_life, payment_amt_64th_bucket as u8)
                        {
                                let historical_negative_log10_times_2048 = approx::negative_log10_times_2048(cumulative_success_prob_times_billion + 1, 1024 * 1024 * 1024);
                                res = res.saturating_add(Self::combined_penalty_msat(amount_msat,
-                                       historical_negative_log10_times_2048, params.historical_liquidity_penalty_multiplier_msat,
-                                       params.historical_liquidity_penalty_amount_multiplier_msat));
+                                       historical_negative_log10_times_2048, score_params.historical_liquidity_penalty_multiplier_msat,
+                                       score_params.historical_liquidity_penalty_amount_multiplier_msat));
                        } else {
                                // If we don't have any valid points (or, once decayed, we have less than a full
                                // point), redo the non-historical calculation with no liquidity bounds tracked and
@@ -1067,8 +1095,8 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
                                let negative_log10_times_2048 =
                                        approx::negative_log10_times_2048(numerator, denominator);
                                res = res.saturating_add(Self::combined_penalty_msat(amount_msat, negative_log10_times_2048,
-                                       params.historical_liquidity_penalty_multiplier_msat,
-                                       params.historical_liquidity_penalty_amount_multiplier_msat));
+                                       score_params.historical_liquidity_penalty_multiplier_msat,
+                                       score_params.historical_liquidity_penalty_amount_multiplier_msat));
                        }
                }
 
@@ -1110,14 +1138,29 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
        }
 
        fn decayed_offset_msat(&self, offset_msat: u64) -> u64 {
-               self.now.duration_since(*self.last_updated).as_secs()
-                       .checked_div(self.params.liquidity_offset_half_life.as_secs())
-                       .and_then(|decays| offset_msat.checked_shr(decays as u32))
-                       .unwrap_or(0)
+               let half_life = self.decay_params.liquidity_offset_half_life.as_secs();
+               if half_life != 0 {
+                       // Decay the offset by the appropriate number of half lives. If half of the next half
+                       // life has passed, approximate an additional three-quarter life to help smooth out the
+                       // decay.
+                       let elapsed_time = self.now.duration_since(*self.last_updated).as_secs();
+                       let half_decays = elapsed_time / (half_life / 2);
+                       let decays = half_decays / 2;
+                       let decayed_offset_msat = offset_msat.checked_shr(decays as u32).unwrap_or(0);
+                       if half_decays % 2 == 0 {
+                               decayed_offset_msat
+                       } else {
+                               // 11_585 / 16_384 ~= core::f64::consts::FRAC_1_SQRT_2
+                               // 16_384 == 2^14
+                               (decayed_offset_msat as u128 * 11_585 / 16_384) as u64
+                       }
+               } else {
+                       0
+               }
        }
 }
 
-impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTracker>, T: Time, U: DerefMut<Target = T>> DirectedChannelLiquidity<'_, L, BRT, T, U> {
+impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTracker>, T: Time, U: DerefMut<Target = T>> DirectedChannelLiquidity<L, BRT, T, U> {
        /// Adjusts the channel liquidity balance bounds when failing to route `amount_msat`.
        fn failed_at_channel<Log: Deref>(&mut self, amount_msat: u64, chan_descr: fmt::Arguments, logger: &Log) where Log::Target: Logger {
                let existing_max_msat = self.max_liquidity_msat();
@@ -1154,7 +1197,7 @@ impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTrac
 
        fn update_history_buckets(&mut self) {
                let half_lives = self.now.duration_since(*self.last_updated).as_secs()
-                       .checked_div(self.params.historical_no_updates_half_life.as_secs())
+                       .checked_div(self.decay_params.historical_no_updates_half_life.as_secs())
                        .map(|v| v.try_into().unwrap_or(u32::max_value())).unwrap_or(u32::max_value());
                self.min_liquidity_offset_history.time_decay_data(half_lives);
                self.max_liquidity_offset_history.time_decay_data(half_lives);
@@ -1193,21 +1236,24 @@ impl<L: DerefMut<Target = u64>, BRT: DerefMut<Target = HistoricalBucketRangeTrac
 }
 
 impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
+       type ScoreParams = ProbabilisticScoringFeeParameters;
        fn channel_penalty_msat(
-               &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage
+               &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &ProbabilisticScoringFeeParameters
        ) -> u64 {
-               if let Some(penalty) = self.params.manual_node_penalties.get(target) {
+               if let Some(penalty) = score_params.manual_node_penalties.get(target) {
                        return *penalty;
                }
 
-               let base_penalty_msat = self.params.base_penalty_msat.saturating_add(
-                       self.params.base_penalty_amount_multiplier_msat
+               let base_penalty_msat = score_params.base_penalty_msat.saturating_add(
+                       score_params.base_penalty_amount_multiplier_msat
                                .saturating_mul(usage.amount_msat) / BASE_AMOUNT_PENALTY_DIVISOR);
 
                let mut anti_probing_penalty_msat = 0;
                match usage.effective_capacity {
-                       EffectiveCapacity::ExactLiquidity { liquidity_msat } => {
-                               if usage.amount_msat > liquidity_msat {
+                       EffectiveCapacity::ExactLiquidity { liquidity_msat: amount_msat } |
+                               EffectiveCapacity::HintMaxHTLC { amount_msat } =>
+                       {
+                               if usage.amount_msat > amount_msat {
                                        return u64::max_value();
                                } else {
                                        return base_penalty_msat;
@@ -1215,7 +1261,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
                        },
                        EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat } => {
                                if htlc_maximum_msat >= capacity_msat/2 {
-                                       anti_probing_penalty_msat = self.params.anti_probing_penalty_msat;
+                                       anti_probing_penalty_msat = score_params.anti_probing_penalty_msat;
                                }
                        },
                        _ => {},
@@ -1227,8 +1273,8 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
                self.channel_liquidities
                        .get(&short_channel_id)
                        .unwrap_or(&ChannelLiquidity::new())
-                       .as_directed(source, target, inflight_htlc_msat, capacity_msat, &self.params)
-                       .penalty_msat(amount_msat, &self.params)
+                       .as_directed(source, target, inflight_htlc_msat, capacity_msat, self.decay_params)
+                       .penalty_msat(amount_msat, score_params)
                        .saturating_add(anti_probing_penalty_msat)
                        .saturating_add(base_penalty_msat)
        }
@@ -1255,13 +1301,13 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
                                        self.channel_liquidities
                                                .entry(hop.short_channel_id)
                                                .or_insert_with(ChannelLiquidity::new)
-                                               .as_directed_mut(source, &target, 0, capacity_msat, &self.params)
+                                               .as_directed_mut(source, &target, 0, capacity_msat, self.decay_params)
                                                .failed_at_channel(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
                                } else {
                                        self.channel_liquidities
                                                .entry(hop.short_channel_id)
                                                .or_insert_with(ChannelLiquidity::new)
-                                               .as_directed_mut(source, &target, 0, capacity_msat, &self.params)
+                                               .as_directed_mut(source, &target, 0, capacity_msat, self.decay_params)
                                                .failed_downstream(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
                                }
                        } else {
@@ -1289,7 +1335,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
                                self.channel_liquidities
                                        .entry(hop.short_channel_id)
                                        .or_insert_with(ChannelLiquidity::new)
-                                       .as_directed_mut(source, &target, 0, capacity_msat, &self.params)
+                                       .as_directed_mut(source, &target, 0, capacity_msat, self.decay_params)
                                        .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).",
@@ -1628,18 +1674,18 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Writeable for Probab
 }
 
 impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time>
-ReadableArgs<(ProbabilisticScoringParameters, G, L)> for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
+ReadableArgs<(ProbabilisticScoringDecayParameters, G, L)> for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
        #[inline]
        fn read<R: Read>(
-               r: &mut R, args: (ProbabilisticScoringParameters, G, L)
+               r: &mut R, args: (ProbabilisticScoringDecayParameters, G, L)
        ) -> Result<Self, DecodeError> {
-               let (params, network_graph, logger) = args;
+               let (decay_params, network_graph, logger) = args;
                let mut channel_liquidities = HashMap::new();
                read_tlv_fields!(r, {
                        (0, channel_liquidities, required),
                });
                Ok(Self {
-                       params,
+                       decay_params,
                        network_graph,
                        logger,
                        channel_liquidities,
@@ -1701,7 +1747,7 @@ impl<T: Time> Readable for ChannelLiquidity<T> {
 
 #[cfg(test)]
 mod tests {
-       use super::{ChannelLiquidity, HistoricalBucketRangeTracker, ProbabilisticScoringParameters, ProbabilisticScorerUsingTime};
+       use super::{ChannelLiquidity, HistoricalBucketRangeTracker, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters, ProbabilisticScorerUsingTime};
        use crate::blinded_path::{BlindedHop, BlindedPath};
        use crate::util::config::UserConfig;
        use crate::util::time::Time;
@@ -1874,8 +1920,8 @@ mod tests {
                let logger = TestLogger::new();
                let last_updated = SinceEpoch::now();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters::default();
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger)
+               let decay_params = ProbabilisticScoringDecayParameters::default();
+               let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
                        .with_channel(42,
                                ChannelLiquidity {
                                        min_liquidity_offset_msat: 700, max_liquidity_offset_msat: 100, last_updated,
@@ -1897,52 +1943,52 @@ mod tests {
                // Update minimum liquidity.
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&source, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 100);
                assert_eq!(liquidity.max_liquidity_msat(), 300);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&target, &source, 0, 1_000, &scorer.params);
+                       .as_directed(&target, &source, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 700);
                assert_eq!(liquidity.max_liquidity_msat(), 900);
 
                scorer.channel_liquidities.get_mut(&42).unwrap()
-                       .as_directed_mut(&source, &target, 0, 1_000, &scorer.params)
+                       .as_directed_mut(&source, &target, 0, 1_000, decay_params)
                        .set_min_liquidity_msat(200);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&source, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 200);
                assert_eq!(liquidity.max_liquidity_msat(), 300);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&target, &source, 0, 1_000, &scorer.params);
+                       .as_directed(&target, &source, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 700);
                assert_eq!(liquidity.max_liquidity_msat(), 800);
 
                // Update maximum liquidity.
 
                let liquidity = scorer.channel_liquidities.get(&43).unwrap()
-                       .as_directed(&target, &recipient, 0, 1_000, &scorer.params);
+                       .as_directed(&target, &recipient, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 700);
                assert_eq!(liquidity.max_liquidity_msat(), 900);
 
                let liquidity = scorer.channel_liquidities.get(&43).unwrap()
-                       .as_directed(&recipient, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&recipient, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 100);
                assert_eq!(liquidity.max_liquidity_msat(), 300);
 
                scorer.channel_liquidities.get_mut(&43).unwrap()
-                       .as_directed_mut(&target, &recipient, 0, 1_000, &scorer.params)
+                       .as_directed_mut(&target, &recipient, 0, 1_000, decay_params)
                        .set_max_liquidity_msat(200);
 
                let liquidity = scorer.channel_liquidities.get(&43).unwrap()
-                       .as_directed(&target, &recipient, 0, 1_000, &scorer.params);
+                       .as_directed(&target, &recipient, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 0);
                assert_eq!(liquidity.max_liquidity_msat(), 200);
 
                let liquidity = scorer.channel_liquidities.get(&43).unwrap()
-                       .as_directed(&recipient, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&recipient, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 800);
                assert_eq!(liquidity.max_liquidity_msat(), 1000);
        }
@@ -1952,8 +1998,8 @@ mod tests {
                let logger = TestLogger::new();
                let last_updated = SinceEpoch::now();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters::default();
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger)
+               let decay_params = ProbabilisticScoringDecayParameters::default();
+               let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
                        .with_channel(42,
                                ChannelLiquidity {
                                        min_liquidity_offset_msat: 200, max_liquidity_offset_msat: 400, last_updated,
@@ -1966,42 +2012,42 @@ mod tests {
 
                // Check initial bounds.
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&source, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 400);
                assert_eq!(liquidity.max_liquidity_msat(), 800);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&target, &source, 0, 1_000, &scorer.params);
+                       .as_directed(&target, &source, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 200);
                assert_eq!(liquidity.max_liquidity_msat(), 600);
 
                // Reset from source to target.
                scorer.channel_liquidities.get_mut(&42).unwrap()
-                       .as_directed_mut(&source, &target, 0, 1_000, &scorer.params)
+                       .as_directed_mut(&source, &target, 0, 1_000, decay_params)
                        .set_min_liquidity_msat(900);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&source, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 900);
                assert_eq!(liquidity.max_liquidity_msat(), 1_000);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&target, &source, 0, 1_000, &scorer.params);
+                       .as_directed(&target, &source, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 0);
                assert_eq!(liquidity.max_liquidity_msat(), 100);
 
                // Reset from target to source.
                scorer.channel_liquidities.get_mut(&42).unwrap()
-                       .as_directed_mut(&target, &source, 0, 1_000, &scorer.params)
+                       .as_directed_mut(&target, &source, 0, 1_000, decay_params)
                        .set_min_liquidity_msat(400);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&source, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 0);
                assert_eq!(liquidity.max_liquidity_msat(), 600);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&target, &source, 0, 1_000, &scorer.params);
+                       .as_directed(&target, &source, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 400);
                assert_eq!(liquidity.max_liquidity_msat(), 1_000);
        }
@@ -2011,8 +2057,8 @@ mod tests {
                let logger = TestLogger::new();
                let last_updated = SinceEpoch::now();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters::default();
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger)
+               let decay_params = ProbabilisticScoringDecayParameters::default();
+               let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
                        .with_channel(42,
                                ChannelLiquidity {
                                        min_liquidity_offset_msat: 200, max_liquidity_offset_msat: 400, last_updated,
@@ -2025,42 +2071,42 @@ mod tests {
 
                // Check initial bounds.
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&source, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 400);
                assert_eq!(liquidity.max_liquidity_msat(), 800);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&target, &source, 0, 1_000, &scorer.params);
+                       .as_directed(&target, &source, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 200);
                assert_eq!(liquidity.max_liquidity_msat(), 600);
 
                // Reset from source to target.
                scorer.channel_liquidities.get_mut(&42).unwrap()
-                       .as_directed_mut(&source, &target, 0, 1_000, &scorer.params)
+                       .as_directed_mut(&source, &target, 0, 1_000, decay_params)
                        .set_max_liquidity_msat(300);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&source, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 0);
                assert_eq!(liquidity.max_liquidity_msat(), 300);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&target, &source, 0, 1_000, &scorer.params);
+                       .as_directed(&target, &source, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 700);
                assert_eq!(liquidity.max_liquidity_msat(), 1_000);
 
                // Reset from target to source.
                scorer.channel_liquidities.get_mut(&42).unwrap()
-                       .as_directed_mut(&target, &source, 0, 1_000, &scorer.params)
+                       .as_directed_mut(&target, &source, 0, 1_000, decay_params)
                        .set_max_liquidity_msat(600);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&source, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 400);
                assert_eq!(liquidity.max_liquidity_msat(), 1_000);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&target, &source, 0, 1_000, &scorer.params);
+                       .as_directed(&target, &source, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 0);
                assert_eq!(liquidity.max_liquidity_msat(), 600);
        }
@@ -2069,11 +2115,12 @@ mod tests {
        fn increased_penalty_nearing_liquidity_upper_bound() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let decay_params = ProbabilisticScoringDecayParameters::default();
+               let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
 
@@ -2082,32 +2129,32 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 10_240, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 102_400, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 47);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 47);
                let usage = ChannelUsage { amount_msat: 1_023_999, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 2_000);
 
                let usage = ChannelUsage {
                        amount_msat: 128,
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 58);
                let usage = ChannelUsage { amount_msat: 256, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 125);
                let usage = ChannelUsage { amount_msat: 374, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 198);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 198);
                let usage = ChannelUsage { amount_msat: 512, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
                let usage = ChannelUsage { amount_msat: 640, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 425);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 425);
                let usage = ChannelUsage { amount_msat: 768, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 602);
                let usage = ChannelUsage { amount_msat: 896, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 902);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 902);
        }
 
        #[test]
@@ -2115,12 +2162,15 @@ mod tests {
                let logger = TestLogger::new();
                let last_updated = SinceEpoch::now();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
                        considered_impossible_penalty_msat: u64::max_value(),
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
+               };
+               let decay_params = ProbabilisticScoringDecayParameters {
+                       ..ProbabilisticScoringDecayParameters::zero_penalty()
                };
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger)
+               let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger)
                        .with_channel(42,
                                ChannelLiquidity {
                                        min_liquidity_offset_msat: 40, max_liquidity_offset_msat: 40, last_updated,
@@ -2135,23 +2185,23 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 100, htlc_maximum_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 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());
+               assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
+               assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
                let usage = ChannelUsage { amount_msat: 61, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
        }
 
        #[test]
        fn does_not_further_penalize_own_channel() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
                let sender = sender_node_id();
                let source = source_node_id();
                let usage = ChannelUsage {
@@ -2162,24 +2212,24 @@ 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, &sender, &source, usage), 301);
+               assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, &params), 301);
 
                scorer.payment_path_failed(&failed_path, 41);
-               assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301);
+               assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, &params), 301);
 
                scorer.payment_path_successful(&successful_path);
-               assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301);
+               assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, &params), 301);
        }
 
        #[test]
        fn sets_liquidity_lower_bound_on_downstream_failure() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
                let path = payment_path_for_amount(500);
@@ -2189,32 +2239,32 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 128);
                let usage = ChannelUsage { amount_msat: 500, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 301);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 301);
                let usage = ChannelUsage { amount_msat: 750, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 602);
 
                scorer.payment_path_failed(&path, 43);
 
                let usage = ChannelUsage { amount_msat: 250, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 500, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 750, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
        }
 
        #[test]
        fn sets_liquidity_upper_bound_on_failure() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
                        considered_impossible_penalty_msat: u64::max_value(),
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
                let path = payment_path_for_amount(500);
@@ -2224,20 +2274,20 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 128);
                let usage = ChannelUsage { amount_msat: 500, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 301);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 301);
                let usage = ChannelUsage { amount_msat: 750, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 602);
 
                scorer.payment_path_failed(&path, 42);
 
                let usage = ChannelUsage { amount_msat: 250, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
                let usage = ChannelUsage { amount_msat: 500, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
                let usage = ChannelUsage { amount_msat: 750, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
        }
 
        #[test]
@@ -2274,39 +2324,39 @@ mod tests {
                let node_c = NodeId::from_pubkey(&pub_c);
                let node_d = NodeId::from_pubkey(&pub_d);
 
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
 
                let usage = ChannelUsage {
                        amount_msat: 250,
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage), 128);
+               assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage, &params), 128);
                // Note that a default liquidity bound is used for B -> C as no channel exists
-               assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage), 128);
-               assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage), 128);
+               assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage, &params), 128);
+               assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage, &params), 128);
 
                scorer.payment_path_failed(&Path { hops: path, blinded_tail: None }, 43);
 
-               assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage), 80);
+               assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage, &params), 80);
                // Note that a default liquidity bound is used for B -> C as no channel exists
-               assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage), 128);
-               assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage), 128);
+               assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage, &params), 128);
+               assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage, &params), 128);
        }
 
        #[test]
        fn reduces_liquidity_upper_bound_along_path_on_success() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
                let sender = sender_node_id();
                let source = source_node_id();
                let target = target_node_id();
@@ -2317,28 +2367,31 @@ mod tests {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
                };
 
-               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);
+               assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, &params), 128);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 128);
+               assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage, &params), 128);
 
                scorer.payment_path_successful(&payment_path_for_amount(500));
 
-               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);
+               assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, &params), 128);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
+               assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage, &params), 300);
        }
 
        #[test]
        fn decays_liquidity_bounds_over_time() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
-                       liquidity_offset_half_life: Duration::from_secs(10),
                        considered_impossible_penalty_msat: u64::max_value(),
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
+               };
+               let decay_params = ProbabilisticScoringDecayParameters {
+                       liquidity_offset_half_life: Duration::from_secs(10),
+                       ..ProbabilisticScoringDecayParameters::zero_penalty()
                };
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
 
@@ -2347,77 +2400,94 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 1_023, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 2_000);
 
                scorer.payment_path_failed(&payment_path_for_amount(768), 42);
                scorer.payment_path_failed(&payment_path_for_amount(128), 43);
 
+               // Initial penalties
                let usage = ChannelUsage { amount_msat: 128, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 256, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 93);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 93);
                let usage = ChannelUsage { amount_msat: 768, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_479);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1_479);
                let usage = ChannelUsage { amount_msat: 896, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
 
-               SinceEpoch::advance(Duration::from_secs(9));
+               // No decay
+               SinceEpoch::advance(Duration::from_secs(4));
                let usage = ChannelUsage { amount_msat: 128, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 256, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 93);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 93);
                let usage = ChannelUsage { amount_msat: 768, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_479);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1_479);
                let usage = ChannelUsage { amount_msat: 896, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
 
+               // Half decay (i.e., three-quarter life)
                SinceEpoch::advance(Duration::from_secs(1));
+               let usage = ChannelUsage { amount_msat: 128, ..usage };
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 22);
+               let usage = ChannelUsage { amount_msat: 256, ..usage };
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 106);
+               let usage = ChannelUsage { amount_msat: 768, ..usage };
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 916);
+               let usage = ChannelUsage { amount_msat: 896, ..usage };
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+
+               // One decay (i.e., half life)
+               SinceEpoch::advance(Duration::from_secs(5));
                let usage = ChannelUsage { amount_msat: 64, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 128, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 34);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 34);
                let usage = ChannelUsage { amount_msat: 896, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_970);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1_970);
                let usage = ChannelUsage { amount_msat: 960, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
 
                // Fully decay liquidity lower bound.
                SinceEpoch::advance(Duration::from_secs(10 * 7));
                let usage = ChannelUsage { amount_msat: 0, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 1, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 1_023, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 2_000);
                let usage = ChannelUsage { amount_msat: 1_024, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
 
                // Fully decay liquidity upper bound.
                SinceEpoch::advance(Duration::from_secs(10));
                let usage = ChannelUsage { amount_msat: 0, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 1_024, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
 
                SinceEpoch::advance(Duration::from_secs(10));
                let usage = ChannelUsage { amount_msat: 0, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 1_024, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
        }
 
        #[test]
        fn decays_liquidity_bounds_without_shift_overflow() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
+               };
+               let decay_params = ProbabilisticScoringDecayParameters {
                        liquidity_offset_half_life: Duration::from_secs(10),
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringDecayParameters::default()
                };
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
                let usage = ChannelUsage {
@@ -2425,30 +2495,33 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 125);
 
                scorer.payment_path_failed(&payment_path_for_amount(512), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 281);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 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, &source, &target, usage), 125);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 125);
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 125);
        }
 
        #[test]
        fn restricts_liquidity_bounds_after_decay() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
+               };
+               let decay_params = ProbabilisticScoringDecayParameters {
                        liquidity_offset_half_life: Duration::from_secs(10),
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringDecayParameters::default()
                };
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
                let usage = ChannelUsage {
@@ -2457,43 +2530,46 @@ mod tests {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
                };
 
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
 
                // More knowledge gives higher confidence (256, 768), meaning a lower penalty.
                scorer.payment_path_failed(&payment_path_for_amount(768), 42);
                scorer.payment_path_failed(&payment_path_for_amount(256), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 281);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 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, &source, &target, usage), 291);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 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));
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 331);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 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), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 245);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 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, &source, &target, usage), 280);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 280);
        }
 
        #[test]
        fn restores_persisted_liquidity_bounds() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
-                       liquidity_offset_half_life: Duration::from_secs(10),
                        considered_impossible_penalty_msat: u64::max_value(),
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
+               };
+               let decay_params = ProbabilisticScoringDecayParameters {
+                       liquidity_offset_half_life: Duration::from_secs(10),
+                       ..ProbabilisticScoringDecayParameters::default()
                };
-               let mut scorer = ProbabilisticScorer::new(params.clone(), &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
                let usage = ChannelUsage {
@@ -2503,34 +2579,37 @@ mod tests {
                };
 
                scorer.payment_path_failed(&payment_path_for_amount(500), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 473);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 473);
 
                scorer.payment_path_failed(&payment_path_for_amount(250), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 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 =
-                       <ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap();
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+                       <ProbabilisticScorer>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
+               assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
        }
 
        #[test]
        fn decays_persisted_liquidity_bounds() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
-                       liquidity_offset_half_life: Duration::from_secs(10),
                        considered_impossible_penalty_msat: u64::max_value(),
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
+               };
+               let decay_params = ProbabilisticScoringDecayParameters {
+                       liquidity_offset_half_life: Duration::from_secs(10),
+                       ..ProbabilisticScoringDecayParameters::zero_penalty()
                };
-               let mut scorer = ProbabilisticScorer::new(params.clone(), &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
                let usage = ChannelUsage {
@@ -2540,7 +2619,7 @@ mod tests {
                };
 
                scorer.payment_path_failed(&payment_path_for_amount(500), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
 
                let mut serialized_scorer = Vec::new();
                scorer.write(&mut serialized_scorer).unwrap();
@@ -2549,14 +2628,14 @@ mod tests {
 
                let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
                let deserialized_scorer =
-                       <ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap();
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 473);
+                       <ProbabilisticScorer>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
+               assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, &params), 473);
 
                scorer.payment_path_failed(&payment_path_for_amount(250), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 365);
+               assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, &params), 365);
        }
 
        #[test]
@@ -2565,8 +2644,8 @@ mod tests {
                // 50k sat reserve).
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters::default();
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let params = ProbabilisticScoringFeeParameters::default();
+               let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
 
@@ -2575,47 +2654,47 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 950_000_000, htlc_maximum_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 4375);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 4375);
                let usage = ChannelUsage {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2739);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 2739);
                let usage = ChannelUsage {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 2_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2236);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 2236);
                let usage = ChannelUsage {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 3_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1983);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1983);
                let usage = ChannelUsage {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 4_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1637);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1637);
                let usage = ChannelUsage {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 5_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1606);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1606);
                let usage = ChannelUsage {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 6_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1331);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1331);
                let usage = ChannelUsage {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_450_000_000, htlc_maximum_msat: 1_000 }, ..usage
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1387);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1387);
                let usage = ChannelUsage {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1379);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1379);
                let usage = ChannelUsage {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 8_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1363);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1363);
                let usage = ChannelUsage {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 9_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1355);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1355);
        }
 
        #[test]
@@ -2630,28 +2709,28 @@ mod tests {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
                };
 
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58);
+               let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 58);
 
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
-                       anti_probing_penalty_msat: 0, ..ProbabilisticScoringParameters::zero_penalty()
+                       anti_probing_penalty_msat: 0, ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 558);
+               let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 558);
 
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
                        base_penalty_amount_multiplier_msat: (1 << 30),
-                       anti_probing_penalty_msat: 0, ..ProbabilisticScoringParameters::zero_penalty()
+                       anti_probing_penalty_msat: 0, ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
 
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 558 + 128);
+               let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 558 + 128);
        }
 
        #[test]
@@ -2666,21 +2745,21 @@ mod tests {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
                };
 
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
                        liquidity_penalty_amount_multiplier_msat: 0,
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+               let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
 
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
                        liquidity_penalty_amount_multiplier_msat: 256,
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 337);
+               let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 337);
        }
 
        #[test]
@@ -2695,23 +2774,24 @@ mod tests {
                        effective_capacity: EffectiveCapacity::Infinite,
                };
 
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 40_000,
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 80_000);
+               let decay_params = ProbabilisticScoringDecayParameters::zero_penalty();
+               let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 80_000);
        }
 
        #[test]
        fn accounts_for_inflight_htlc_usage() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        considered_impossible_penalty_msat: u64::max_value(),
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
 
@@ -2720,18 +2800,18 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
                };
-               assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
 
                let usage = ChannelUsage { inflight_htlc_msat: 251, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
        }
 
        #[test]
        fn removes_uncertainity_when_exact_liquidity_known() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters::default();
-               let scorer = ProbabilisticScorer::new(params.clone(), &network_graph, &logger);
+               let params = ProbabilisticScoringFeeParameters::default();
+               let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
 
@@ -2741,27 +2821,29 @@ mod tests {
                        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);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), base_penalty_msat);
 
                let usage = ChannelUsage { amount_msat: 1_000, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), base_penalty_msat);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), base_penalty_msat);
 
                let usage = ChannelUsage { amount_msat: 1_001, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
        }
 
        #[test]
        fn remembers_historical_failures() {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
-                       liquidity_offset_half_life: Duration::from_secs(60 * 60),
+               let params = ProbabilisticScoringFeeParameters {
                        historical_liquidity_penalty_multiplier_msat: 1024,
                        historical_liquidity_penalty_amount_multiplier_msat: 1024,
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
+               };
+               let decay_params = ProbabilisticScoringDecayParameters {
+                       liquidity_offset_half_life: Duration::from_secs(60 * 60),
                        historical_no_updates_half_life: Duration::from_secs(10),
-                       ..ProbabilisticScoringParameters::zero_penalty()
                };
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
 
@@ -2771,12 +2853,12 @@ mod tests {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
                };
                // With no historical data the normal liquidity penalty calculation is used.
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 47);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 47);
                assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
                        None);
 
                scorer.payment_path_failed(&payment_path_for_amount(1), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2048);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 2048);
                // The "it failed" increment is 32, where the probability should lie fully in the first
                // octile.
                assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
@@ -2785,7 +2867,7 @@ mod tests {
                // Even after we tell the scorer we definitely have enough available liquidity, it will
                // still remember that there was some failure in the past, and assign a non-0 penalty.
                scorer.payment_path_failed(&payment_path_for_amount(1000), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 198);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 198);
                // The first octile should be decayed just slightly and the last octile has a new point.
                assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
                        Some(([31, 0, 0, 0, 0, 0, 0, 32], [31, 0, 0, 0, 0, 0, 0, 32])));
@@ -2793,7 +2875,7 @@ mod tests {
                // Advance the time forward 16 half-lives (which the docs claim will ensure all data is
                // gone), and check that we're back to where we started.
                SinceEpoch::advance(Duration::from_secs(10 * 16));
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 47);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 47);
                // Once fully decayed we still have data, but its all-0s. In the future we may remove the
                // data entirely instead.
                assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
@@ -2805,14 +2887,14 @@ mod tests {
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
                };
                scorer.payment_path_failed(&payment_path_for_amount(1), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 409);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 409);
 
                let usage = ChannelUsage {
                        amount_msat: 1,
                        inflight_htlc_msat: 0,
-                       effective_capacity: EffectiveCapacity::MaximumHTLC { amount_msat: 0 },
+                       effective_capacity: EffectiveCapacity::AdvertisedMaxHTLC { amount_msat: 0 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &target, &source, usage), 2048);
+               assert_eq!(scorer.channel_penalty_msat(42, &target, &source, usage, &params), 2048);
 
                // Advance to decay all liquidity offsets to zero.
                SinceEpoch::advance(Duration::from_secs(60 * 60 * 10));
@@ -2833,11 +2915,11 @@ mod tests {
                let network_graph = network_graph(&logger);
                let source = source_node_id();
                let target = target_node_id();
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        anti_probing_penalty_msat: 500,
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
 
                // Check we receive no penalty for a low htlc_maximum_msat.
                let usage = ChannelUsage {
@@ -2845,7 +2927,7 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
 
                // Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity.
                let usage = ChannelUsage {
@@ -2853,7 +2935,7 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_024_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 500);
 
                // Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity/2.
                let usage = ChannelUsage {
@@ -2861,7 +2943,7 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 512_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 500);
 
                // Check we receive no anti-probing penalty for htlc_maximum_msat == channel_capacity/2 - 1.
                let usage = ChannelUsage {
@@ -2869,7 +2951,7 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 511_999 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
        }
 
        #[test]
@@ -2877,12 +2959,12 @@ mod tests {
                // Make sure we'll account for a blinded path's final_value_msat in scoring
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
-               let params = ProbabilisticScoringParameters {
+               let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
-                       liquidity_offset_half_life: Duration::from_secs(10),
-                       ..ProbabilisticScoringParameters::zero_penalty()
+                       ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
-               let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
+               let decay_params = ProbabilisticScoringDecayParameters::default();
+               let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
                let target = target_node_id();
                let usage = ChannelUsage {
@@ -2890,7 +2972,7 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
+               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
 
                let mut path = payment_path_for_amount(768);
                let recipient_hop = path.hops.pop().unwrap();
@@ -2917,7 +2999,7 @@ mod tests {
                scorer.payment_path_failed(&path, 43);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, &scorer.params);
+                       .as_directed(&source, &target, 0, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 256);
                assert_eq!(liquidity.max_liquidity_msat(), 768);
        }