Merge pull request #2752 from valentinewallace/2023-11-large-final-onion-payload...
[rust-lightning] / lightning / src / routing / scoring.rs
index b13f5531ec77f970e7fa4f9be330d93386af0142..337fd8eec7a895ce5198edb3a9774267048de03d 100644 (file)
@@ -10,7 +10,7 @@
 //! Utilities for scoring payment channels.
 //!
 //! [`ProbabilisticScorer`] may be given to [`find_route`] to score payment channels during path
-//! finding when a custom [`Score`] implementation is not needed.
+//! finding when a custom [`ScoreLookUp`] implementation is not needed.
 //!
 //! # Example
 //!
@@ -26,7 +26,7 @@
 //! #
 //! # struct FakeLogger {};
 //! # impl Logger for FakeLogger {
-//! #     fn log(&self, record: &Record) { unimplemented!() }
+//! #     fn log(&self, record: Record) { unimplemented!() }
 //! # }
 //! # fn find_scored_route(payer: PublicKey, route_params: RouteParameters, network_graph: NetworkGraph<&FakeLogger>) {
 //! # let logger = FakeLogger {};
 
 use crate::ln::msgs::DecodeError;
 use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId};
-use crate::routing::router::Path;
+use crate::routing::router::{Path, CandidateRouteHop};
 use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
 use crate::util::logger::Logger;
 use crate::util::time::Time;
 
 use crate::prelude::*;
 use core::{cmp, fmt};
-use core::cell::{RefCell, RefMut};
+use core::cell::{RefCell, RefMut, Ref};
 use core::convert::TryInto;
 use core::ops::{Deref, DerefMut};
 use core::time::Duration;
 use crate::io::{self, Read};
-use crate::sync::{Mutex, MutexGuard};
+use crate::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
 
 /// We define Score ever-so-slightly differently based on whether we are being built for C bindings
 /// or not. For users, `LockableScore` must somehow be writeable to disk. For Rust users, this is
@@ -86,8 +86,10 @@ use crate::sync::{Mutex, MutexGuard};
 macro_rules! define_score { ($($supertrait: path)*) => {
 /// An interface used to score payment channels for path finding.
 ///
-///    Scoring is in terms of fees willing to be paid in order to avoid routing through a channel.
-pub trait Score $(: $supertrait)* {
+/// `ScoreLookUp` is used to determine the penalty for a given channel.
+///
+/// Scoring is in terms of fees willing to be paid in order to avoid routing through a channel.
+pub trait ScoreLookUp {
        /// 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.
@@ -101,9 +103,12 @@ 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, score_params: &Self::ScoreParams
+               &self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams
        ) -> u64;
+}
 
+/// `ScoreUpdate` is used to update the scorer's internal state after a payment attempt.
+pub trait ScoreUpdate {
        /// Handles updating channel penalties after failing to route through a channel.
        fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64);
 
@@ -117,14 +122,29 @@ pub trait Score $(: $supertrait)* {
        fn probe_successful(&mut self, path: &Path);
 }
 
-impl<S: Score, T: DerefMut<Target=S> $(+ $supertrait)*> Score for T {
+/// A trait which can both lookup and update routing channel penalty scores.
+///
+/// This is used in places where both bounds are required and implemented for all types which
+/// implement [`ScoreLookUp`] and [`ScoreUpdate`].
+///
+/// Bindings users may need to manually implement this for their custom scoring implementations.
+pub trait Score : ScoreLookUp + ScoreUpdate $(+ $supertrait)* {}
+
+#[cfg(not(c_bindings))]
+impl<T: ScoreLookUp + ScoreUpdate $(+ $supertrait)*> Score for T {}
+
+#[cfg(not(c_bindings))]
+impl<S: ScoreLookUp, T: Deref<Target=S>> ScoreLookUp for T {
        type ScoreParams = S::ScoreParams;
        fn channel_penalty_msat(
-               &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams
+               &self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams
        ) -> u64 {
-               self.deref().channel_penalty_msat(short_channel_id, source, target, usage, score_params)
+               self.deref().channel_penalty_msat(candidate, usage, score_params)
        }
+}
 
+#[cfg(not(c_bindings))]
+impl<S: ScoreUpdate, T: DerefMut<Target=S>> ScoreUpdate for T {
        fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) {
                self.deref_mut().payment_path_failed(path, short_channel_id)
        }
@@ -145,26 +165,35 @@ impl<S: Score, T: DerefMut<Target=S> $(+ $supertrait)*> Score for T {
 
 #[cfg(c_bindings)]
 define_score!(Writeable);
+
 #[cfg(not(c_bindings))]
 define_score!();
 
 /// A scorer that is accessed under a lock.
 ///
-/// Needed so that calls to [`Score::channel_penalty_msat`] in [`find_route`] can be made while
-/// having shared ownership of a scorer but without requiring internal locking in [`Score`]
+/// Needed so that calls to [`ScoreLookUp::channel_penalty_msat`] in [`find_route`] can be made while
+/// having shared ownership of a scorer but without requiring internal locking in [`ScoreUpdate`]
 /// implementations. Internal locking would be detrimental to route finding performance and could
-/// result in [`Score::channel_penalty_msat`] returning a different value for the same channel.
+/// result in [`ScoreLookUp::channel_penalty_msat`] returning a different value for the same channel.
 ///
 /// [`find_route`]: crate::routing::router::find_route
 pub trait LockableScore<'a> {
-       /// The [`Score`] type.
-       type Score: 'a + Score;
+       /// The [`ScoreUpdate`] type.
+       type ScoreUpdate: 'a + ScoreUpdate;
+       /// The [`ScoreLookUp`] type.
+       type ScoreLookUp: 'a + ScoreLookUp;
 
-       /// The locked [`Score`] type.
-       type Locked: DerefMut<Target = Self::Score> + Sized;
+       /// The write locked [`ScoreUpdate`] type.
+       type WriteLocked: DerefMut<Target = Self::ScoreUpdate> + Sized;
 
-       /// Returns the locked scorer.
-       fn lock(&'a self) -> Self::Locked;
+       /// The read locked [`ScoreLookUp`] type.
+       type ReadLocked: Deref<Target = Self::ScoreLookUp> + Sized;
+
+       /// Returns read locked scorer.
+       fn read_lock(&'a self) -> Self::ReadLocked;
+
+       /// Returns write locked scorer.
+       fn write_lock(&'a self) -> Self::WriteLocked;
 }
 
 /// Refers to a scorer that is accessible under lock and also writeable to disk
@@ -176,89 +205,166 @@ pub trait WriteableScore<'a>: LockableScore<'a> + Writeable {}
 #[cfg(not(c_bindings))]
 impl<'a, T> WriteableScore<'a> for T where T: LockableScore<'a> + Writeable {}
 #[cfg(not(c_bindings))]
-impl<'a, T: 'a + Score> LockableScore<'a> for Mutex<T> {
-       type Score = T;
-       type Locked = MutexGuard<'a, T>;
+impl<'a, T: Score + 'a> LockableScore<'a> for Mutex<T> {
+       type ScoreUpdate = T;
+       type ScoreLookUp = T;
+
+       type WriteLocked = MutexGuard<'a, Self::ScoreUpdate>;
+       type ReadLocked = MutexGuard<'a, Self::ScoreLookUp>;
+
+       fn read_lock(&'a self) -> Self::ReadLocked {
+               Mutex::lock(self).unwrap()
+       }
 
-       fn lock(&'a self) -> Self::Locked {
+       fn write_lock(&'a self) -> Self::WriteLocked {
                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>;
+impl<'a, T: Score + 'a> LockableScore<'a> for RefCell<T> {
+       type ScoreUpdate = T;
+       type ScoreLookUp = T;
+
+       type WriteLocked = RefMut<'a, Self::ScoreUpdate>;
+       type ReadLocked = Ref<'a, Self::ScoreLookUp>;
 
-       fn lock(&'a self) -> Self::Locked {
+       fn write_lock(&'a self) -> Self::WriteLocked {
                self.borrow_mut()
        }
+
+       fn read_lock(&'a self) -> Self::ReadLocked {
+               self.borrow()
+       }
+}
+
+#[cfg(not(c_bindings))]
+impl<'a, T: Score + 'a> LockableScore<'a> for RwLock<T> {
+       type ScoreUpdate = T;
+       type ScoreLookUp = T;
+
+       type WriteLocked = RwLockWriteGuard<'a, Self::ScoreLookUp>;
+       type ReadLocked = RwLockReadGuard<'a, Self::ScoreUpdate>;
+
+       fn read_lock(&'a self) -> Self::ReadLocked {
+               RwLock::read(self).unwrap()
+       }
+
+       fn write_lock(&'a self) -> Self::WriteLocked {
+               RwLock::write(self).unwrap()
+       }
 }
 
 #[cfg(c_bindings)]
 /// A concrete implementation of [`LockableScore`] which supports multi-threading.
 pub struct MultiThreadedLockableScore<T: Score> {
-       score: Mutex<T>,
+       score: RwLock<T>,
 }
 
 #[cfg(c_bindings)]
-impl<'a, T: 'a + Score> LockableScore<'a> for MultiThreadedLockableScore<T> {
-       type Score = T;
-       type Locked = MultiThreadedScoreLock<'a, T>;
+impl<'a, T: Score + 'a> LockableScore<'a> for MultiThreadedLockableScore<T> {
+       type ScoreUpdate = T;
+       type ScoreLookUp = T;
+       type WriteLocked = MultiThreadedScoreLockWrite<'a, Self::ScoreUpdate>;
+       type ReadLocked = MultiThreadedScoreLockRead<'a, Self::ScoreLookUp>;
 
-       fn lock(&'a self) -> Self::Locked {
-               MultiThreadedScoreLock(Mutex::lock(&self.score).unwrap())
+       fn read_lock(&'a self) -> Self::ReadLocked {
+               MultiThreadedScoreLockRead(self.score.read().unwrap())
+       }
+
+       fn write_lock(&'a self) -> Self::WriteLocked {
+               MultiThreadedScoreLockWrite(self.score.write().unwrap())
        }
 }
 
 #[cfg(c_bindings)]
 impl<T: Score> Writeable for MultiThreadedLockableScore<T> {
        fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
-               self.lock().write(writer)
+               self.score.read().unwrap().write(writer)
        }
 }
 
 #[cfg(c_bindings)]
-impl<'a, T: 'a + Score> WriteableScore<'a> for MultiThreadedLockableScore<T> {}
+impl<'a, T: Score + 'a> WriteableScore<'a> for MultiThreadedLockableScore<T> {}
 
 #[cfg(c_bindings)]
 impl<T: Score> MultiThreadedLockableScore<T> {
        /// Creates a new [`MultiThreadedLockableScore`] given an underlying [`Score`].
        pub fn new(score: T) -> Self {
-               MultiThreadedLockableScore { score: Mutex::new(score) }
+               MultiThreadedLockableScore { score: RwLock::new(score) }
        }
 }
 
 #[cfg(c_bindings)]
 /// A locked `MultiThreadedLockableScore`.
-pub struct MultiThreadedScoreLock<'a, T: Score>(MutexGuard<'a, T>);
+pub struct MultiThreadedScoreLockRead<'a, T: Score>(RwLockReadGuard<'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> {
-               self.0.write(writer)
+/// A locked `MultiThreadedLockableScore`.
+pub struct MultiThreadedScoreLockWrite<'a, T: Score>(RwLockWriteGuard<'a, T>);
+
+#[cfg(c_bindings)]
+impl<'a, T: 'a + Score> Deref for MultiThreadedScoreLockRead<'a, T> {
+       type Target = T;
+
+       fn deref(&self) -> &Self::Target {
+               self.0.deref()
        }
 }
 
 #[cfg(c_bindings)]
-impl<'a, T: 'a + Score> DerefMut for MultiThreadedScoreLock<'a, T> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        self.0.deref_mut()
-    }
+impl<'a, T: Score> ScoreLookUp for MultiThreadedScoreLockRead<'a, T> {
+       type ScoreParams = T::ScoreParams;
+       fn channel_penalty_msat(&self, candidate:&CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams
+       ) -> u64 {
+               self.0.channel_penalty_msat(candidate, usage, score_params)
+       }
 }
 
 #[cfg(c_bindings)]
-impl<'a, T: 'a + Score> Deref for MultiThreadedScoreLock<'a, T> {
+impl<'a, T: Score> Writeable for MultiThreadedScoreLockWrite<'a, T> {
+       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+               self.0.write(writer)
+       }
+}
+
+#[cfg(c_bindings)]
+impl<'a, T: 'a + Score> Deref for MultiThreadedScoreLockWrite<'a, T> {
        type Target = T;
 
-    fn deref(&self) -> &Self::Target {
-        self.0.deref()
-    }
+       fn deref(&self) -> &Self::Target {
+               self.0.deref()
+       }
+}
+
+#[cfg(c_bindings)]
+impl<'a, T: 'a + Score> DerefMut for MultiThreadedScoreLockWrite<'a, T> {
+       fn deref_mut(&mut self) -> &mut Self::Target {
+               self.0.deref_mut()
+       }
 }
 
+#[cfg(c_bindings)]
+impl<'a, T: Score> ScoreUpdate for MultiThreadedScoreLockWrite<'a, T> {
+       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)
+       }
+}
 
 
-/// Proposed use of a channel passed as a parameter to [`Score::channel_penalty_msat`].
+/// Proposed use of a channel passed as a parameter to [`ScoreLookUp::channel_penalty_msat`].
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub struct ChannelUsage {
        /// The amount to send through the channel, denominated in millisatoshis.
@@ -273,7 +379,7 @@ pub struct ChannelUsage {
 }
 
 #[derive(Clone)]
-/// [`Score`] implementation that uses a fixed penalty.
+/// [`ScoreLookUp`] implementation that uses a fixed penalty.
 pub struct FixedPenaltyScorer {
        penalty_msat: u64,
 }
@@ -285,12 +391,14 @@ impl FixedPenaltyScorer {
        }
 }
 
-impl Score for FixedPenaltyScorer {
+impl ScoreLookUp for FixedPenaltyScorer {
        type ScoreParams = ();
-       fn channel_penalty_msat(&self, _: u64, _: &NodeId, _: &NodeId, _: ChannelUsage, _score_params: &Self::ScoreParams) -> u64 {
+       fn channel_penalty_msat(&self, _: &CandidateRouteHop, _: ChannelUsage, _score_params: &Self::ScoreParams) -> u64 {
                self.penalty_msat
        }
+}
 
+impl ScoreUpdate for FixedPenaltyScorer {
        fn payment_path_failed(&mut self, _path: &Path, _short_channel_id: u64) {}
 
        fn payment_path_successful(&mut self, _path: &Path) {}
@@ -323,7 +431,7 @@ use crate::util::time::Eternity;
 #[cfg(feature = "no-std")]
 type ConfiguredTime = Eternity;
 
-/// [`Score`] implementation using channel success probability distributions.
+/// [`ScoreLookUp`] implementation using channel success probability distributions.
 ///
 /// Channels are tracked with upper and lower liquidity bounds - when an HTLC fails at a channel,
 /// we learn that the upper-bound on the available liquidity is lower than the amount of the HTLC.
@@ -361,7 +469,7 @@ type ConfiguredTime = Eternity;
 /// [`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.
+/// Probabilistic [`ScoreLookUp`] implementation.
 ///
 /// 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>
@@ -387,12 +495,13 @@ pub struct ProbabilisticScoringFeeParameters {
        /// Default value: 500 msat
        pub base_penalty_msat: u64,
 
-       /// A multiplier used with the payment amount to calculate a fixed penalty applied to each
-       /// channel, in excess of the [`base_penalty_msat`].
+       /// A multiplier used with the total amount flowing over a channel to calculate a fixed penalty
+       /// applied to each channel, in excess of the [`base_penalty_msat`].
        ///
        /// The purpose of the amount penalty is to avoid having fees dominate the channel cost (i.e.,
        /// fees plus penalty) for large payments. The penalty is computed as the product of this
-       /// multiplier and `2^30`ths of the payment amount.
+       /// multiplier and `2^30`ths of the total amount flowing over a channel (i.e. the payment
+       /// amount plus the amount of any other HTLCs flowing we sent over the same channel).
        ///
        /// ie `base_penalty_amount_multiplier_msat * amount_msat / 2^30`
        ///
@@ -419,14 +528,14 @@ pub struct ProbabilisticScoringFeeParameters {
        /// [`liquidity_offset_half_life`]: ProbabilisticScoringDecayParameters::liquidity_offset_half_life
        pub liquidity_penalty_multiplier_msat: u64,
 
-       /// 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.
+       /// A multiplier used in conjunction with the total amount flowing over a channel 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.
        ///
        /// The purpose of the amount penalty is to avoid having fees dominate the channel cost (i.e.,
        /// fees plus penalty) for large payments. The penalty is computed as the product of this
-       /// multiplier and `2^20`ths of the payment amount, weighted by the negative `log10` of the
-       /// success probability.
+       /// multiplier and `2^20`ths of the amount flowing over this channel, weighted by the negative
+       /// `log10` of the success probability.
        ///
        /// `-log10(success_probability) * liquidity_penalty_amount_multiplier_msat * amount_msat / 2^20`
        ///
@@ -455,13 +564,15 @@ pub struct ProbabilisticScoringFeeParameters {
        /// [`liquidity_penalty_multiplier_msat`]: Self::liquidity_penalty_multiplier_msat
        pub historical_liquidity_penalty_multiplier_msat: u64,
 
-       /// A multiplier used in conjunction with the payment amount and the negative `log10` of the
-       /// channel's success probability for the payment, as determined based on the history of our
-       /// estimates of the channel's available liquidity, to determine a penalty.
+       /// A multiplier used in conjunction with the total amount flowing over a channel and the
+       /// negative `log10` of the channel's success probability for the payment, as determined based
+       /// on the history of our estimates of the channel's available liquidity, to determine a
+       /// penalty.
        ///
        /// The purpose of the amount penalty is to avoid having fees dominate the channel cost for
-       /// large payments. The penalty is computed as the product of this multiplier and the `2^20`ths
-       /// of the payment amount, weighted by the negative `log10` of the success probability.
+       /// large payments. The penalty is computed as the product of this multiplier and `2^20`ths
+       /// of the amount flowing over this channel, weighted by the negative `log10` of the success
+       /// probability.
        ///
        /// This penalty is similar to [`liquidity_penalty_amount_multiplier_msat`], however, instead
        /// of using only our latest estimate for the current liquidity available in the channel, it
@@ -491,8 +602,9 @@ pub struct ProbabilisticScoringFeeParameters {
        /// Default value: 250 msat
        pub anti_probing_penalty_msat: u64,
 
-       /// This penalty is applied when the amount we're attempting to send over a channel exceeds our
-       /// current estimate of the channel's available liquidity.
+       /// This penalty is applied when the total amount flowing over a channel exceeds our current
+       /// estimate of the channel's available liquidity. The total amount is the amount of the
+       /// current HTLC plus any HTLCs which we've sent over the same channel.
        ///
        /// Note that in this case all other penalties, including the
        /// [`liquidity_penalty_multiplier_msat`] and [`liquidity_penalty_amount_multiplier_msat`]-based
@@ -509,6 +621,28 @@ pub struct ProbabilisticScoringFeeParameters {
        /// [`base_penalty_msat`]: Self::base_penalty_msat
        /// [`anti_probing_penalty_msat`]: Self::anti_probing_penalty_msat
        pub considered_impossible_penalty_msat: u64,
+
+       /// In order to calculate most of the scores above, we must first convert a lower and upper
+       /// bound on the available liquidity in a channel into the probability that we think a payment
+       /// will succeed. That probability is derived from a Probability Density Function for where we
+       /// think the liquidity in a channel likely lies, given such bounds.
+       ///
+       /// If this flag is set, that PDF is simply a constant - we assume that the actual available
+       /// liquidity in a channel is just as likely to be at any point between our lower and upper
+       /// bounds.
+       ///
+       /// If this flag is *not* set, that PDF is `(x - 0.5*capacity) ^ 2`. That is, we use an
+       /// exponential curve which expects the liquidity of a channel to lie "at the edges". This
+       /// matches experimental results - most routing nodes do not aggressively rebalance their
+       /// channels and flows in the network are often unbalanced, leaving liquidity usually
+       /// unavailable.
+       ///
+       /// Thus, for the "best" routes, leave this flag `false`. However, the flag does imply a number
+       /// of floating-point multiplications in the hottest routing code, which may lead to routing
+       /// performance degradation on some machines.
+       ///
+       /// Default value: false
+       pub linear_success_probability: bool,
 }
 
 impl Default for ProbabilisticScoringFeeParameters {
@@ -523,6 +657,7 @@ impl Default for ProbabilisticScoringFeeParameters {
                        considered_impossible_penalty_msat: 1_0000_0000_000,
                        historical_liquidity_penalty_multiplier_msat: 10_000,
                        historical_liquidity_penalty_amount_multiplier_msat: 64,
+                       linear_success_probability: false,
                }
        }
 }
@@ -576,6 +711,7 @@ impl ProbabilisticScoringFeeParameters {
                        manual_node_penalties: HashMap::new(),
                        anti_probing_penalty_msat: 0,
                        considered_impossible_penalty_msat: 0,
+                       linear_success_probability: true,
                }
        }
 }
@@ -666,7 +802,6 @@ struct DirectedChannelLiquidity<L: Deref<Target = u64>, BRT: Deref<Target = Hist
        min_liquidity_offset_msat: L,
        max_liquidity_offset_msat: L,
        liquidity_history: HistoricalMinMaxBuckets<BRT>,
-       inflight_htlc_msat: u64,
        capacity_msat: u64,
        last_updated: U,
        now: T,
@@ -704,7 +839,7 @@ 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.decay_params);
+                                               let dir_liq = liq.as_directed(source, target, amt, self.decay_params);
 
                                                let (min_buckets, max_buckets) = dir_liq.liquidity_history
                                                        .get_decayed_buckets(now, *dir_liq.last_updated,
@@ -758,7 +893,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.decay_params);
+                                       let dir_liq = liq.as_directed(source, target, amt, self.decay_params);
                                        return Some((dir_liq.min_liquidity_msat(), dir_liq.max_liquidity_msat()));
                                }
                        }
@@ -800,7 +935,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.decay_params);
+                                       let dir_liq = liq.as_directed(source, target, amt, self.decay_params);
 
                                        let (min_buckets, mut max_buckets) =
                                                dir_liq.liquidity_history.get_decayed_buckets(
@@ -826,7 +961,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ProbabilisticScorerU
        /// [`Self::historical_estimated_channel_liquidity_probabilities`] (but not those returned by
        /// [`Self::estimated_channel_liquidity_range`]).
        pub fn historical_estimated_payment_success_probability(
-               &self, scid: u64, target: &NodeId, amount_msat: u64)
+               &self, scid: u64, target: &NodeId, amount_msat: u64, params: &ProbabilisticScoringFeeParameters)
        -> Option<f64> {
                let graph = self.network_graph.read_only();
 
@@ -834,11 +969,12 @@ 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 capacity_msat = directed_info.effective_capacity().as_msat();
-                                       let dir_liq = liq.as_directed(source, target, 0, capacity_msat, self.decay_params);
+                                       let dir_liq = liq.as_directed(source, target, capacity_msat, self.decay_params);
 
                                        return dir_liq.liquidity_history.calculate_success_probability_times_billion(
                                                dir_liq.now, *dir_liq.last_updated,
-                                               self.decay_params.historical_no_updates_half_life, amount_msat, capacity_msat
+                                               self.decay_params.historical_no_updates_half_life, &params, amount_msat,
+                                               capacity_msat
                                        ).map(|p| p as f64 / (1024 * 1024 * 1024) as f64);
                                }
                        }
@@ -862,7 +998,7 @@ impl<T: Time> ChannelLiquidity<T> {
        /// Returns a view of the channel liquidity directed from `source` to `target` assuming
        /// `capacity_msat`.
        fn as_directed(
-               &self, source: &NodeId, target: &NodeId, inflight_htlc_msat: u64, capacity_msat: u64, decay_params: ProbabilisticScoringDecayParameters
+               &self, source: &NodeId, target: &NodeId, 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 {
@@ -880,7 +1016,6 @@ impl<T: Time> ChannelLiquidity<T> {
                                min_liquidity_offset_history,
                                max_liquidity_offset_history,
                        },
-                       inflight_htlc_msat,
                        capacity_msat,
                        last_updated: &self.last_updated,
                        now: T::now(),
@@ -891,7 +1026,7 @@ impl<T: Time> ChannelLiquidity<T> {
        /// Returns a mutable view of the channel liquidity directed from `source` to `target` assuming
        /// `capacity_msat`.
        fn as_directed_mut(
-               &mut self, source: &NodeId, target: &NodeId, inflight_htlc_msat: u64, capacity_msat: u64, decay_params: ProbabilisticScoringDecayParameters
+               &mut self, source: &NodeId, target: &NodeId, 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 {
@@ -909,7 +1044,6 @@ impl<T: Time> ChannelLiquidity<T> {
                                min_liquidity_offset_history,
                                max_liquidity_offset_history,
                        },
-                       inflight_htlc_msat,
                        capacity_msat,
                        last_updated: &mut self.last_updated,
                        now: T::now(),
@@ -930,11 +1064,80 @@ 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;
 
+/// Raises three `f64`s to the 3rd power, without `powi` because it requires `std` (dunno why).
+#[inline(always)]
+fn three_f64_pow_3(a: f64, b: f64, c: f64) -> (f64, f64, f64) {
+       (a * a * a, b * b * b, c * c * c)
+}
+
+/// Given liquidity bounds, calculates the success probability (in the form of a numerator and
+/// denominator) of an HTLC. This is a key assumption in our scoring models.
+///
+/// Must not return a numerator or denominator greater than 2^31 for arguments less than 2^31.
+///
+/// min_zero_implies_no_successes signals that a `min_liquidity_msat` of 0 means we've not
+/// (recently) seen an HTLC successfully complete over this channel.
+#[inline(always)]
+fn success_probability(
+       amount_msat: u64, min_liquidity_msat: u64, max_liquidity_msat: u64, capacity_msat: u64,
+       params: &ProbabilisticScoringFeeParameters, min_zero_implies_no_successes: bool,
+) -> (u64, u64) {
+       debug_assert!(min_liquidity_msat <= amount_msat);
+       debug_assert!(amount_msat < max_liquidity_msat);
+       debug_assert!(max_liquidity_msat <= capacity_msat);
+
+       let (numerator, mut denominator) =
+               if params.linear_success_probability {
+                       (max_liquidity_msat - amount_msat,
+                               (max_liquidity_msat - min_liquidity_msat).saturating_add(1))
+               } else {
+                       let capacity = capacity_msat as f64;
+                       let min = (min_liquidity_msat as f64) / capacity;
+                       let max = (max_liquidity_msat as f64) / capacity;
+                       let amount = (amount_msat as f64) / capacity;
+
+                       // Assume the channel has a probability density function of (x - 0.5)^2 for values from
+                       // 0 to 1 (where 1 is the channel's full capacity). The success probability given some
+                       // liquidity bounds is thus the integral under the curve from the amount to maximum
+                       // estimated liquidity, divided by the same integral from the minimum to the maximum
+                       // estimated liquidity bounds.
+                       //
+                       // Because the integral from x to y is simply (y - 0.5)^3 - (x - 0.5)^3, we can
+                       // calculate the cumulative density function between the min/max bounds trivially. Note
+                       // that we don't bother to normalize the CDF to total to 1, as it will come out in the
+                       // division of num / den.
+                       let (max_pow, amt_pow, min_pow) = three_f64_pow_3(max - 0.5, amount - 0.5, min - 0.5);
+                       let num = max_pow - amt_pow;
+                       let den = max_pow - min_pow;
+
+                       // Because our numerator and denominator max out at 0.5^3 we need to multiply them by
+                       // quite a large factor to get something useful (ideally in the 2^30 range).
+                       const BILLIONISH: f64 = 1024.0 * 1024.0 * 1024.0;
+                       let numerator = (num * BILLIONISH) as u64 + 1;
+                       let denominator = (den * BILLIONISH) as u64 + 1;
+                       debug_assert!(numerator <= 1 << 30, "Got large numerator ({}) from float {}.", numerator, num);
+                       debug_assert!(denominator <= 1 << 30, "Got large denominator ({}) from float {}.", denominator, den);
+                       (numerator, denominator)
+               };
+
+       if min_zero_implies_no_successes && min_liquidity_msat == 0 &&
+               denominator < u64::max_value() / 21
+       {
+               // If we have no knowledge of the channel, scale probability down by ~75%
+               // Note that we prefer to increase the denominator rather than decrease the numerator as
+               // the denominator is more likely to be larger and thus provide greater precision. This is
+               // mostly an overoptimization but makes a large difference in tests.
+               denominator = denominator * 21 / 16
+       }
+
+       (numerator, denominator)
+}
+
 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, score_params: &ProbabilisticScoringFeeParameters) -> u64 {
-               let available_capacity = self.available_capacity();
+               let available_capacity = self.capacity_msat;
                let max_liquidity_msat = self.max_liquidity_msat();
                let min_liquidity_msat = core::cmp::min(self.min_liquidity_msat(), max_liquidity_msat);
 
@@ -950,9 +1153,9 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
                                        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);
-                       if amount_msat - min_liquidity_msat < denominator / PRECISION_LOWER_BOUND_DENOMINATOR {
+                       let (numerator, denominator) = success_probability(amount_msat,
+                               min_liquidity_msat, max_liquidity_msat, available_capacity, score_params, false);
+                       if denominator - numerator < denominator / PRECISION_LOWER_BOUND_DENOMINATOR {
                                // If the failure probability is < 1.5625% (as 1 - numerator/denominator < 1/64),
                                // don't bother trying to use the log approximation as it gets too noisy to be
                                // particularly helpful, instead just round down to 0.
@@ -979,7 +1182,8 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
                   score_params.historical_liquidity_penalty_amount_multiplier_msat != 0 {
                        if let Some(cumulative_success_prob_times_billion) = self.liquidity_history
                                .calculate_success_probability_times_billion(self.now, *self.last_updated,
-                                       self.decay_params.historical_no_updates_half_life, amount_msat, self.capacity_msat)
+                                       self.decay_params.historical_no_updates_half_life, score_params, amount_msat,
+                                       self.capacity_msat)
                        {
                                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,
@@ -989,9 +1193,8 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
                                // 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
                                // the historical penalty multipliers.
-                               let available_capacity = self.available_capacity();
-                               let numerator = available_capacity.saturating_sub(amount_msat).saturating_add(1);
-                               let denominator = available_capacity.saturating_add(1);
+                               let (numerator, denominator) = success_probability(amount_msat, 0,
+                                       available_capacity, available_capacity, score_params, true);
                                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,
@@ -1030,16 +1233,10 @@ impl<L: Deref<Target = u64>, BRT: Deref<Target = HistoricalBucketRangeTracker>,
        /// Returns the upper bound of the channel liquidity balance in this direction.
        #[inline(always)]
        fn max_liquidity_msat(&self) -> u64 {
-               self.available_capacity()
+               self.capacity_msat
                        .saturating_sub(self.decayed_offset_msat(*self.max_liquidity_offset_msat))
        }
 
-       /// Returns the capacity minus the in-flight HTLCs in this direction.
-       #[inline(always)]
-       fn available_capacity(&self) -> u64 {
-               self.capacity_msat.saturating_sub(self.inflight_htlc_msat)
-       }
-
        fn decayed_offset_msat(&self, offset_msat: u64) -> u64 {
                let half_life = self.decay_params.liquidity_offset_half_life.as_secs();
                if half_life != 0 {
@@ -1142,12 +1339,19 @@ 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 {
+impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreLookUp 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, score_params: &ProbabilisticScoringFeeParameters
+               &self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &ProbabilisticScoringFeeParameters
        ) -> u64 {
-               if let Some(penalty) = score_params.manual_node_penalties.get(target) {
+               let (scid, target) = match candidate {
+                       CandidateRouteHop::PublicHop { info, short_channel_id } => {
+                               (short_channel_id, info.target())
+                       },
+                       _ => return 0,
+               };
+               let source = candidate.source();
+               if let Some(penalty) = score_params.manual_node_penalties.get(&target) {
                        return *penalty;
                }
 
@@ -1174,18 +1378,19 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
                        _ => {},
                }
 
-               let amount_msat = usage.amount_msat;
+               let amount_msat = usage.amount_msat.saturating_add(usage.inflight_htlc_msat);
                let capacity_msat = usage.effective_capacity.as_msat();
-               let inflight_htlc_msat = usage.inflight_htlc_msat;
                self.channel_liquidities
-                       .get(&short_channel_id)
+                       .get(&scid)
                        .unwrap_or(&ChannelLiquidity::new())
-                       .as_directed(source, target, inflight_htlc_msat, capacity_msat, self.decay_params)
+                       .as_directed(&source, &target, capacity_msat, self.decay_params)
                        .penalty_msat(amount_msat, score_params)
                        .saturating_add(anti_probing_penalty_msat)
                        .saturating_add(base_penalty_msat)
        }
+}
 
+impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreUpdate for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
        fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) {
                let amount_msat = path.final_value_msat();
                log_trace!(self.logger, "Scoring path through to SCID {} as having failed at {} msat", short_channel_id, amount_msat);
@@ -1208,13 +1413,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.decay_params)
+                                               .as_directed_mut(source, &target, 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.decay_params)
+                                               .as_directed_mut(source, &target, capacity_msat, self.decay_params)
                                                .failed_downstream(amount_msat, format_args!("SCID {}, towards {:?}", hop.short_channel_id, target), &self.logger);
                                }
                        } else {
@@ -1242,7 +1447,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.decay_params)
+                                       .as_directed_mut(source, &target, 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).",
@@ -1260,6 +1465,10 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for Probabilis
        }
 }
 
+#[cfg(c_bindings)]
+impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> Score for ProbabilisticScorerUsingTime<G, L, T>
+where L::Target: Logger {}
+
 mod approx {
        const BITS: u32 = 64;
        const HIGHEST_BIT: u32 = BITS - 1;
@@ -1684,6 +1893,10 @@ mod bucketed_history {
                buckets: [u16; 32],
        }
 
+       /// Buckets are stored in fixed point numbers with a 5 bit fractional part. Thus, the value
+       /// "one" is 32, or this constant.
+       pub const BUCKET_FIXED_POINT_ONE: u16 = 32;
+
        impl HistoricalBucketRangeTracker {
                pub(super) fn new() -> Self { Self { buckets: [0; 32] } }
                pub(super) fn track_datapoint(&mut self, liquidity_offset_msat: u64, capacity_msat: u64) {
@@ -1714,7 +1927,7 @@ mod bucketed_history {
                                        *e = ((*e as u32) * 2047 / 2048) as u16;
                                }
                                let bucket = pos_to_bucket(pos);
-                               self.buckets[bucket] = self.buckets[bucket].saturating_add(32);
+                               self.buckets[bucket] = self.buckets[bucket].saturating_add(BUCKET_FIXED_POINT_ONE);
                        }
                }
                /// Decay all buckets by the given number of half-lives. Used to more aggressively remove old
@@ -1768,7 +1981,8 @@ mod bucketed_history {
 
                        // If the total valid points is smaller than 1.0 (i.e. 32 in our fixed-point scheme),
                        // treat it as if we were fully decayed.
-                       if total_valid_points_tracked.checked_shr(required_decays).unwrap_or(0) < 32*32 {
+                       const FULLY_DECAYED: u16 = BUCKET_FIXED_POINT_ONE * BUCKET_FIXED_POINT_ONE;
+                       if total_valid_points_tracked.checked_shr(required_decays).unwrap_or(0) < FULLY_DECAYED.into() {
                                return None;
                        }
 
@@ -1777,8 +1991,9 @@ mod bucketed_history {
 
                #[inline]
                pub(super) fn calculate_success_probability_times_billion<T: Time>(
-                       &self, now: T, last_updated: T, half_life: Duration, amount_msat: u64, capacity_msat: u64)
-               -> Option<u64> {
+                       &self, now: T, last_updated: T, half_life: Duration,
+                       params: &ProbabilisticScoringFeeParameters, amount_msat: u64, capacity_msat: u64
+               ) -> Option<u64> {
                        // If historical penalties are enabled, we try to calculate a probability of success
                        // given our historical distribution of min- and max-liquidity bounds in a channel.
                        // To do so, we walk the set of historical liquidity bucket (min, max) combinations
@@ -1806,21 +2021,20 @@ mod bucketed_history {
                                let mut highest_max_bucket_with_points = 0; // The highest max-bucket with any data
                                let mut total_max_points = 0; // Total points in max-buckets to consider
                                for (max_idx, max_bucket) in self.max_liquidity_offset_history.buckets.iter().enumerate() {
-                                       if *max_bucket >= 32 {
+                                       if *max_bucket >= BUCKET_FIXED_POINT_ONE {
                                                highest_max_bucket_with_points = cmp::max(highest_max_bucket_with_points, max_idx);
                                        }
                                        total_max_points += *max_bucket as u64;
                                }
                                let max_bucket_end_pos = BUCKET_START_POS[32 - highest_max_bucket_with_points] - 1;
                                if payment_pos < max_bucket_end_pos {
+                                       let (numerator, denominator) = success_probability(payment_pos as u64, 0,
+                                               max_bucket_end_pos as u64, POSITION_TICKS as u64 - 1, params, true);
                                        let bucket_prob_times_billion =
                                                (self.min_liquidity_offset_history.buckets[0] as u64) * total_max_points
                                                        * 1024 * 1024 * 1024 / total_valid_points_tracked;
                                        cumulative_success_prob_times_billion += bucket_prob_times_billion *
-                                               ((max_bucket_end_pos - payment_pos) as u64) /
-                                               // Add an additional one in the divisor as the payment bucket has been
-                                               // rounded down.
-                                               (max_bucket_end_pos + 1) as u64;
+                                               numerator / denominator;
                                }
                        }
 
@@ -1838,11 +2052,11 @@ mod bucketed_history {
                                        } else if payment_pos < min_bucket_start_pos {
                                                cumulative_success_prob_times_billion += bucket_prob_times_billion;
                                        } else {
+                                               let (numerator, denominator) = success_probability(payment_pos as u64,
+                                                       min_bucket_start_pos as u64, max_bucket_end_pos as u64,
+                                                       POSITION_TICKS as u64 - 1, params, true);
                                                cumulative_success_prob_times_billion += bucket_prob_times_billion *
-                                                       ((max_bucket_end_pos - payment_pos) as u64) /
-                                                       // Add an additional one in the divisor as the payment bucket has been
-                                                       // rounded down.
-                                                       ((max_bucket_end_pos - min_bucket_start_pos + 1) as u64);
+                                                       numerator / denominator;
                                        }
                                }
                        }
@@ -1966,12 +2180,12 @@ mod tests {
        use crate::ln::channelmanager;
        use crate::ln::msgs::{ChannelAnnouncement, ChannelUpdate, UnsignedChannelAnnouncement, UnsignedChannelUpdate};
        use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId};
-       use crate::routing::router::{BlindedTail, Path, RouteHop};
-       use crate::routing::scoring::{ChannelUsage, Score};
+       use crate::routing::router::{BlindedTail, Path, RouteHop, CandidateRouteHop};
+       use crate::routing::scoring::{ChannelUsage, ScoreLookUp, ScoreUpdate};
        use crate::util::ser::{ReadableArgs, Writeable};
        use crate::util::test_utils::{self, TestLogger};
 
-       use bitcoin::blockdata::constants::genesis_block;
+       use bitcoin::blockdata::constants::ChainHash;
        use bitcoin::hashes::Hash;
        use bitcoin::hashes::sha256d::Hash as Sha256dHash;
        use bitcoin::network::constants::Network;
@@ -2028,10 +2242,6 @@ mod tests {
                PublicKey::from_secret_key(&secp_ctx, &recipient_privkey())
        }
 
-       fn sender_node_id() -> NodeId {
-               NodeId::from_pubkey(&sender_pubkey())
-       }
-
        fn recipient_node_id() -> NodeId {
                NodeId::from_pubkey(&recipient_pubkey())
        }
@@ -2048,7 +2258,7 @@ mod tests {
                network_graph: &mut NetworkGraph<&TestLogger>, short_channel_id: u64, node_1_key: SecretKey,
                node_2_key: SecretKey
        ) {
-               let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
+               let genesis_hash = ChainHash::using_genesis_block(Network::Testnet);
                let node_1_secret = &SecretKey::from_slice(&[39; 32]).unwrap();
                let node_2_secret = &SecretKey::from_slice(&[40; 32]).unwrap();
                let secp_ctx = Secp256k1::new();
@@ -2081,7 +2291,7 @@ mod tests {
                network_graph: &mut NetworkGraph<&TestLogger>, short_channel_id: u64, node_key: SecretKey,
                flags: u8, htlc_maximum_msat: u64, timestamp: u32,
        ) {
-               let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
+               let genesis_hash = ChainHash::using_genesis_block(Network::Testnet);
                let secp_ctx = Secp256k1::new();
                let unsigned_update = UnsignedChannelUpdate {
                        chain_hash: genesis_hash,
@@ -2112,6 +2322,7 @@ mod tests {
                        channel_features: channelmanager::provided_channel_features(&config),
                        fee_msat,
                        cltv_expiry_delta: 18,
+                       maybe_announced_channel: true,
                }
        }
 
@@ -2153,52 +2364,52 @@ mod tests {
                // Update minimum liquidity.
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, decay_params);
+                       .as_directed(&source, &target, 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, decay_params);
+                       .as_directed(&target, &source, 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, decay_params)
+                       .as_directed_mut(&source, &target, 1_000, decay_params)
                        .set_min_liquidity_msat(200);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, decay_params);
+                       .as_directed(&source, &target, 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, decay_params);
+                       .as_directed(&target, &source, 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, decay_params);
+                       .as_directed(&target, &recipient, 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, decay_params);
+                       .as_directed(&recipient, &target, 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, decay_params)
+                       .as_directed_mut(&target, &recipient, 1_000, decay_params)
                        .set_max_liquidity_msat(200);
 
                let liquidity = scorer.channel_liquidities.get(&43).unwrap()
-                       .as_directed(&target, &recipient, 0, 1_000, decay_params);
+                       .as_directed(&target, &recipient, 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, decay_params);
+                       .as_directed(&recipient, &target, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 800);
                assert_eq!(liquidity.max_liquidity_msat(), 1000);
        }
@@ -2222,42 +2433,42 @@ mod tests {
 
                // Check initial bounds.
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, decay_params);
+                       .as_directed(&source, &target, 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, decay_params);
+                       .as_directed(&target, &source, 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, decay_params)
+                       .as_directed_mut(&source, &target, 1_000, decay_params)
                        .set_min_liquidity_msat(900);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, decay_params);
+                       .as_directed(&source, &target, 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, decay_params);
+                       .as_directed(&target, &source, 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, decay_params)
+                       .as_directed_mut(&target, &source, 1_000, decay_params)
                        .set_min_liquidity_msat(400);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, decay_params);
+                       .as_directed(&source, &target, 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, decay_params);
+                       .as_directed(&target, &source, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 400);
                assert_eq!(liquidity.max_liquidity_msat(), 1_000);
        }
@@ -2281,42 +2492,42 @@ mod tests {
 
                // Check initial bounds.
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, decay_params);
+                       .as_directed(&source, &target, 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, decay_params);
+                       .as_directed(&target, &source, 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, decay_params)
+                       .as_directed_mut(&source, &target, 1_000, decay_params)
                        .set_max_liquidity_msat(300);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, decay_params);
+                       .as_directed(&source, &target, 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, decay_params);
+                       .as_directed(&target, &source, 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, decay_params)
+                       .as_directed_mut(&target, &source, 1_000, decay_params)
                        .set_max_liquidity_msat(600);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, decay_params);
+                       .as_directed(&source, &target, 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, decay_params);
+                       .as_directed(&target, &source, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 0);
                assert_eq!(liquidity.max_liquidity_msat(), 600);
        }
@@ -2332,39 +2543,45 @@ mod tests {
                let decay_params = ProbabilisticScoringDecayParameters::default();
                let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
-               let target = target_node_id();
 
                let usage = ChannelUsage {
                        amount_msat: 1_024,
                        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, &params), 0);
+               let network_graph = network_graph.read_only();
+               let channel = network_graph.channel(42).unwrap();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 10_240, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 102_400, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 47);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 47);
                let usage = ChannelUsage { amount_msat: 1_023_999, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 58);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 58);
                let usage = ChannelUsage { amount_msat: 256, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 125);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 125);
                let usage = ChannelUsage { amount_msat: 374, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 198);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 198);
                let usage = ChannelUsage { amount_msat: 512, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
                let usage = ChannelUsage { amount_msat: 640, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 425);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 425);
                let usage = ChannelUsage { amount_msat: 768, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 602);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 602);
                let usage = ChannelUsage { amount_msat: 896, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 902);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 902);
        }
 
        #[test]
@@ -2388,19 +2605,24 @@ mod tests {
                                        max_liquidity_offset_history: HistoricalBucketRangeTracker::new(),
                                });
                let source = source_node_id();
-               let target = target_node_id();
 
                let usage = ChannelUsage {
                        amount_msat: 39,
                        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, &params), 0);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 50, ..usage };
-               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());
+               assert_ne!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
+               assert_ne!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
                let usage = ChannelUsage { amount_msat: 61, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
        }
 
        #[test]
@@ -2412,7 +2634,6 @@ mod tests {
                        ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
                let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
-               let sender = sender_node_id();
                let source = source_node_id();
                let usage = ChannelUsage {
                        amount_msat: 500,
@@ -2421,14 +2642,20 @@ mod tests {
                };
                let failed_path = payment_path_for_amount(500);
                let successful_path = payment_path_for_amount(200);
+               let channel = &network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 41,
+               };
 
-               assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, &params), 301);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
 
                scorer.payment_path_failed(&failed_path, 41);
-               assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, &params), 301);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
 
                scorer.payment_path_successful(&successful_path);
-               assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, &params), 301);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
        }
 
        #[test]
@@ -2441,7 +2668,6 @@ mod tests {
                };
                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);
 
                let usage = ChannelUsage {
@@ -2449,20 +2675,26 @@ 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, &params), 128);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
                let usage = ChannelUsage { amount_msat: 500, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 301);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
                let usage = ChannelUsage { amount_msat: 750, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 602);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 500, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 750, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
        }
 
        #[test]
@@ -2476,7 +2708,6 @@ mod tests {
                };
                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);
 
                let usage = ChannelUsage {
@@ -2484,20 +2715,26 @@ 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, &params), 128);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
                let usage = ChannelUsage { amount_msat: 500, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 301);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 301);
                let usage = ChannelUsage { amount_msat: 750, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 602);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 300);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
                let usage = ChannelUsage { amount_msat: 500, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
                let usage = ChannelUsage { amount_msat: 750, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
        }
 
        #[test]
@@ -2532,7 +2769,6 @@ mod tests {
                let node_a = NodeId::from_pubkey(&pub_a);
                let node_b = NodeId::from_pubkey(&pub_b);
                let node_c = NodeId::from_pubkey(&pub_c);
-               let node_d = NodeId::from_pubkey(&pub_d);
 
                let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
@@ -2545,17 +2781,53 @@ 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, &node_a, &node_b, usage, &params), 128);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&node_a).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 128);
-               assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage, &params), 128);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&node_b).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 43,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
+               let channel = network_graph.read_only().channel(44).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&node_c).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 44,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 80);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&node_a).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 128);
-               assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage, &params), 128);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&node_b).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 43,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
+               let channel = network_graph.read_only().channel(44).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&node_c).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 44,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 128);
        }
 
        #[test]
@@ -2567,25 +2839,39 @@ mod tests {
                        ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
                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();
-               let recipient = recipient_node_id();
                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(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);
+               let network_graph = network_graph.read_only().channels().clone();
+               let channel_42 = network_graph.get(&42).unwrap();
+               let channel_43 = network_graph.get(&43).unwrap();
+               let (info, _) = channel_42.as_directed_from(&source).unwrap();
+               let candidate_41 = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 41,
+               };
+               let (info, target) = channel_42.as_directed_from(&source).unwrap();
+               let candidate_42 = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               let (info, _) = channel_43.as_directed_from(&target).unwrap();
+               let candidate_43 = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 43,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate_41, usage, &params), 128);
+               assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, &params), 128);
+               assert_eq!(scorer.channel_penalty_msat(&candidate_43, usage, &params), 128);
 
                scorer.payment_path_successful(&payment_path_for_amount(500));
 
-               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);
+               assert_eq!(scorer.channel_penalty_msat(&candidate_41, usage, &params), 128);
+               assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, &params), 300);
+               assert_eq!(scorer.channel_penalty_msat(&candidate_43, usage, &params), 300);
        }
 
        #[test]
@@ -2603,86 +2889,91 @@ mod tests {
                };
                let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
-               let target = target_node_id();
 
                let usage = ChannelUsage {
                        amount_msat: 0,
                        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, &params), 0);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 1_023, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 256, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 93);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 93);
                let usage = ChannelUsage { amount_msat: 768, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1_479);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 1_479);
                let usage = ChannelUsage { amount_msat: 896, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
 
                // 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, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 256, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 93);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 93);
                let usage = ChannelUsage { amount_msat: 768, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1_479);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 1_479);
                let usage = ChannelUsage { amount_msat: 896, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 22);
                let usage = ChannelUsage { amount_msat: 256, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 106);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 106);
                let usage = ChannelUsage { amount_msat: 768, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 916);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 921);
                let usage = ChannelUsage { amount_msat: 896, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 128, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 34);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 34);
                let usage = ChannelUsage { amount_msat: 896, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 1_970);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 1_970);
                let usage = ChannelUsage { amount_msat: 960, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 1, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 1_023, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 2_000);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2_000);
                let usage = ChannelUsage { amount_msat: 1_024, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 1_024, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
                let usage = ChannelUsage { amount_msat: 1_024, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
        }
 
        #[test]
@@ -2699,24 +2990,29 @@ mod tests {
                };
                let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
-               let target = target_node_id();
                let usage = ChannelUsage {
                        amount_msat: 256,
                        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, &params), 125);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 125);
 
                scorer.payment_path_failed(&payment_path_for_amount(512), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 281);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 125);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 125);
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 125);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 125);
        }
 
        #[test]
@@ -2733,37 +3029,42 @@ mod tests {
                };
                let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
-               let target = target_node_id();
                let usage = ChannelUsage {
                        amount_msat: 512,
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
                };
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
 
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 281);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 291);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 331);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 245);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, 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, &params), 280);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 280);
        }
 
        #[test]
@@ -2781,7 +3082,6 @@ mod tests {
                };
                let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
-               let target = target_node_id();
                let usage = ChannelUsage {
                        amount_msat: 500,
                        inflight_htlc_msat: 0,
@@ -2789,13 +3089,19 @@ mod tests {
                };
 
                scorer.payment_path_failed(&payment_path_for_amount(500), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 473);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 473);
 
                scorer.payment_path_failed(&payment_path_for_amount(250), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
 
                let mut serialized_scorer = Vec::new();
                scorer.write(&mut serialized_scorer).unwrap();
@@ -2803,7 +3109,7 @@ mod tests {
                let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
                let deserialized_scorer =
                        <ProbabilisticScorer>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
+               assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, &params), 300);
        }
 
        #[test]
@@ -2821,7 +3127,6 @@ mod tests {
                };
                let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
-               let target = target_node_id();
                let usage = ChannelUsage {
                        amount_msat: 500,
                        inflight_htlc_msat: 0,
@@ -2829,7 +3134,13 @@ mod tests {
                };
 
                scorer.payment_path_failed(&payment_path_for_amount(500), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
 
                let mut serialized_scorer = Vec::new();
                scorer.write(&mut serialized_scorer).unwrap();
@@ -2839,13 +3150,13 @@ mod tests {
                let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
                let deserialized_scorer =
                        <ProbabilisticScorer>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, &params), 473);
+               assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, &params), 473);
 
                scorer.payment_path_failed(&payment_path_for_amount(250), 43);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
 
                SinceEpoch::advance(Duration::from_secs(10));
-               assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, &params), 365);
+               assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, &params), 370);
        }
 
        #[test]
@@ -2857,54 +3168,59 @@ mod tests {
                let params = ProbabilisticScoringFeeParameters::default();
                let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
                let source = source_node_id();
-               let target = target_node_id();
 
                let usage = ChannelUsage {
                        amount_msat: 100_000_000,
                        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, &params), 4375);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 11497);
                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, &params), 2739);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 7408);
                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, &params), 2236);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 6151);
                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, &params), 1983);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 5427);
                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, &params), 1637);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 4955);
                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, &params), 1606);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 4736);
                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, &params), 1331);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 4484);
                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, &params), 1387);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 4484);
                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, &params), 1379);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 4263);
                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, &params), 1363);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 4263);
                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, &params), 1355);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 4044);
        }
 
        #[test]
@@ -2912,7 +3228,6 @@ mod tests {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
                let source = source_node_id();
-               let target = target_node_id();
                let usage = ChannelUsage {
                        amount_msat: 128,
                        inflight_htlc_msat: 0,
@@ -2924,14 +3239,20 @@ mod tests {
                        ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
                let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 58);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 58);
 
                let params = ProbabilisticScoringFeeParameters {
                        base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
                        anti_probing_penalty_msat: 0, ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
                let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 558);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 558);
 
                let params = ProbabilisticScoringFeeParameters {
                        base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
@@ -2940,7 +3261,7 @@ mod tests {
                };
 
                let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 558 + 128);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 558 + 128);
        }
 
        #[test]
@@ -2948,7 +3269,6 @@ mod tests {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
                let source = source_node_id();
-               let target = target_node_id();
                let usage = ChannelUsage {
                        amount_msat: 512_000,
                        inflight_htlc_msat: 0,
@@ -2961,7 +3281,13 @@ mod tests {
                        ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
                let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 300);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
 
                let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 1_000,
@@ -2969,7 +3295,7 @@ mod tests {
                        ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
                let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 337);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 337);
        }
 
        #[test]
@@ -2977,20 +3303,24 @@ mod tests {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
                let source = source_node_id();
-               let target = target_node_id();
                let usage = ChannelUsage {
                        amount_msat: u64::max_value(),
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Infinite,
                };
-
                let params = ProbabilisticScoringFeeParameters {
                        liquidity_penalty_multiplier_msat: 40_000,
                        ..ProbabilisticScoringFeeParameters::zero_penalty()
                };
                let decay_params = ProbabilisticScoringDecayParameters::zero_penalty();
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
                let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 80_000);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 80_000);
        }
 
        #[test]
@@ -3003,17 +3333,23 @@ mod tests {
                };
                let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
                let source = source_node_id();
-               let target = target_node_id();
 
                let usage = ChannelUsage {
                        amount_msat: 750,
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
                };
-               assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               let network_graph = network_graph.read_only();
+               let channel = network_graph.channel(42).unwrap();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_ne!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
 
                let usage = ChannelUsage { inflight_htlc_msat: 251, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
        }
 
        #[test]
@@ -3023,7 +3359,6 @@ mod tests {
                let params = ProbabilisticScoringFeeParameters::default();
                let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
                let source = source_node_id();
-               let target = target_node_id();
 
                let base_penalty_msat = params.base_penalty_msat;
                let usage = ChannelUsage {
@@ -3031,13 +3366,20 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 },
                };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), base_penalty_msat);
+               let network_graph = network_graph.read_only();
+               let channel = network_graph.channel(42).unwrap();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), base_penalty_msat);
 
                let usage = ChannelUsage { amount_msat: 1_000, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), base_penalty_msat);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), base_penalty_msat);
 
                let usage = ChannelUsage { amount_msat: 1_001, ..usage };
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), u64::max_value());
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), u64::max_value());
        }
 
        #[test]
@@ -3068,30 +3410,60 @@ 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, &params), 47);
+               {
+                       let network_graph = network_graph.read_only();
+                       let channel = network_graph.channel(42).unwrap();
+                       let (info, _) = channel.as_directed_from(&source).unwrap();
+                       let candidate = CandidateRouteHop::PublicHop {
+                               info,
+                               short_channel_id: 42,
+                       };
+
+                       // With no historical data the normal liquidity penalty calculation is used.
+                       assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 168);
+               }
                assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
-                       None);
-               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42),
-                       None);
+               None);
+               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, &params),
+               None);
 
                scorer.payment_path_failed(&payment_path_for_amount(1), 42);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 2048);
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage_1, &params), 128);
+               {
+                       let network_graph = network_graph.read_only();
+                       let channel = network_graph.channel(42).unwrap();
+                       let (info, _) = channel.as_directed_from(&source).unwrap();
+                       let candidate = CandidateRouteHop::PublicHop {
+                               info,
+                               short_channel_id: 42,
+                       };
+
+                       assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2048);
+                       assert_eq!(scorer.channel_penalty_msat(&candidate, usage_1, &params), 249);
+               }
                // The "it failed" increment is 32, where the probability should lie several buckets into
                // the first octile.
                assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
                        Some(([32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                                [0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])));
-               assert!(scorer.historical_estimated_payment_success_probability(42, &target, 1)
+               assert!(scorer.historical_estimated_payment_success_probability(42, &target, 1, &params)
                        .unwrap() > 0.35);
-               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 500),
+               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 500, &params),
                        Some(0.0));
 
                // 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, &params), 32);
+               {
+                       let network_graph = network_graph.read_only();
+                       let channel = network_graph.channel(42).unwrap();
+                       let (info, _) = channel.as_directed_from(&source).unwrap();
+                       let candidate = CandidateRouteHop::PublicHop {
+                               info,
+                               short_channel_id: 42,
+                       };
+
+                       assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 105);
+               }
                // The first points should be decayed just slightly and the last bucket has a new point.
                assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
                        Some(([31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0],
@@ -3100,23 +3472,33 @@ mod tests {
                // The exact success probability is a bit complicated and involves integer rounding, so we
                // simply check bounds here.
                let five_hundred_prob =
-                       scorer.historical_estimated_payment_success_probability(42, &target, 500).unwrap();
-               assert!(five_hundred_prob > 0.66);
-               assert!(five_hundred_prob < 0.68);
+                       scorer.historical_estimated_payment_success_probability(42, &target, 500, &params).unwrap();
+               assert!(five_hundred_prob > 0.59);
+               assert!(five_hundred_prob < 0.60);
                let one_prob =
-                       scorer.historical_estimated_payment_success_probability(42, &target, 1).unwrap();
-               assert!(one_prob < 1.0);
-               assert!(one_prob > 0.95);
+                       scorer.historical_estimated_payment_success_probability(42, &target, 1, &params).unwrap();
+               assert!(one_prob < 0.85);
+               assert!(one_prob > 0.84);
 
                // 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, &params), 47);
+               {
+                       let network_graph = network_graph.read_only();
+                       let channel = network_graph.channel(42).unwrap();
+                       let (info, _) = channel.as_directed_from(&source).unwrap();
+                       let candidate = CandidateRouteHop::PublicHop {
+                               info,
+                               short_channel_id: 42,
+                       };
+
+                       assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 168);
+               }
                // 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),
                        None);
-               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 1), None);
+               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 1, &params), None);
 
                let mut usage = ChannelUsage {
                        amount_msat: 100,
@@ -3124,16 +3506,26 @@ 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, &params), 2048);
-               usage.inflight_htlc_msat = 0;
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 866);
+               {
+                       let network_graph = network_graph.read_only();
+                       let channel = network_graph.channel(42).unwrap();
+                       let (info, _) = channel.as_directed_from(&source).unwrap();
+                       let candidate = CandidateRouteHop::PublicHop {
+                               info,
+                               short_channel_id: 42,
+                       };
 
-               let usage = ChannelUsage {
-                       amount_msat: 1,
-                       inflight_htlc_msat: 0,
-                       effective_capacity: EffectiveCapacity::AdvertisedMaxHTLC { amount_msat: 0 },
-               };
-               assert_eq!(scorer.channel_penalty_msat(42, &target, &source, usage, &params), 2048);
+                       assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2050);
+                       usage.inflight_htlc_msat = 0;
+                       assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 866);
+
+                       let usage = ChannelUsage {
+                               amount_msat: 1,
+                               inflight_htlc_msat: 0,
+                               effective_capacity: EffectiveCapacity::AdvertisedMaxHTLC { amount_msat: 0 },
+                       };
+                       assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 2048);
+               }
 
                // Advance to decay all liquidity offsets to zero.
                SinceEpoch::advance(Duration::from_secs(60 * 60 * 10));
@@ -3153,7 +3545,6 @@ mod tests {
                let logger = TestLogger::new();
                let network_graph = network_graph(&logger);
                let source = source_node_id();
-               let target = target_node_id();
                let params = ProbabilisticScoringFeeParameters {
                        anti_probing_penalty_msat: 500,
                        ..ProbabilisticScoringFeeParameters::zero_penalty()
@@ -3166,7 +3557,14 @@ 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, &params), 0);
+               let network_graph = network_graph.read_only();
+               let channel = network_graph.channel(42).unwrap();
+               let (info, _) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
 
                // Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity.
                let usage = ChannelUsage {
@@ -3174,7 +3572,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, &params), 500);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 500);
 
                // Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity/2.
                let usage = ChannelUsage {
@@ -3182,7 +3580,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, &params), 500);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 500);
 
                // Check we receive no anti-probing penalty for htlc_maximum_msat == channel_capacity/2 - 1.
                let usage = ChannelUsage {
@@ -3190,7 +3588,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, &params), 0);
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 0);
        }
 
        #[test]
@@ -3205,13 +3603,18 @@ mod tests {
                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 {
                        amount_msat: 512,
                        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, &params), 300);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, target) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 300);
 
                let mut path = payment_path_for_amount(768);
                let recipient_hop = path.hops.pop().unwrap();
@@ -3238,7 +3641,7 @@ mod tests {
                scorer.payment_path_failed(&path, 43);
 
                let liquidity = scorer.channel_liquidities.get(&42).unwrap()
-                       .as_directed(&source, &target, 0, 1_000, decay_params);
+                       .as_directed(&source, &target, 1_000, decay_params);
                assert_eq!(liquidity.min_liquidity_msat(), 256);
                assert_eq!(liquidity.max_liquidity_msat(), 768);
        }
@@ -3267,7 +3670,6 @@ mod tests {
 
                let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
                let source = source_node_id();
-               let target = target_node_id();
 
                let mut amount_msat = 10_000_000;
                let usage = ChannelUsage {
@@ -3275,19 +3677,25 @@ mod tests {
                        inflight_htlc_msat: 0,
                        effective_capacity: EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: capacity_msat },
                };
-               // With no historical data the normal liquidity penalty calculation is used, which in this
-               // case is diminuitively low.
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params), 0);
+               let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+               let (info, target) = channel.as_directed_from(&source).unwrap();
+               let candidate = CandidateRouteHop::PublicHop {
+                       info,
+                       short_channel_id: 42,
+               };
+               // With no historical data the normal liquidity penalty calculation is used, which results
+               // in a success probability of ~75%.
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params), 1269);
                assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
                        None);
-               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42),
+               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, &params),
                        None);
 
                // Fail to pay once, and then check the buckets and penalty.
                scorer.payment_path_failed(&payment_path_for_amount(amount_msat), 42);
                // The penalty should be the maximum penalty, as the payment we're scoring is now in the
                // same bucket which is the only maximum datapoint.
-               assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, &params),
+               assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &params),
                        2048 + 2048 * amount_msat / super::AMOUNT_PENALTY_DIVISOR);
                // The "it failed" increment is 32, which we should apply to the first upper-bound (between
                // 6k sats and 12k sats).
@@ -3295,14 +3703,14 @@ mod tests {
                        Some(([32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                                [0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])));
                // The success probability estimate itself should be zero.
-               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat),
+               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params),
                        Some(0.0));
 
                // Now test again with the amount in the bottom bucket.
                amount_msat /= 2;
                // The new amount is entirely within the only minimum bucket with score, so the probability
                // we assign is 1/2.
-               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat),
+               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params),
                        Some(0.5));
 
                // ...but once we see a failure, we consider the payment to be substantially less likely,
@@ -3312,7 +3720,7 @@ mod tests {
                assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
                        Some(([63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                                [32, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])));
-               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat),
+               assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, amount_msat, &params),
                        Some(0.0));
        }
 }