+/// A [`Router`] implemented using [`find_route`].
+pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref, SP: Sized, Sc: Score<ScoreParams = SP>> where
+ L::Target: Logger,
+ S::Target: for <'a> LockableScore<'a, Locked = MutexGuard<'a, Sc>>,
+{
+ network_graph: G,
+ logger: L,
+ random_seed_bytes: Mutex<[u8; 32]>,
+ scorer: S,
+ score_params: SP
+}
+
+impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref, SP: Sized, Sc: Score<ScoreParams = SP>> DefaultRouter<G, L, S, SP, Sc> where
+ L::Target: Logger,
+ S::Target: for <'a> LockableScore<'a, Locked = MutexGuard<'a, Sc>>,
+{
+ /// Creates a new router.
+ pub fn new(network_graph: G, logger: L, random_seed_bytes: [u8; 32], scorer: S, score_params: SP) -> Self {
+ let random_seed_bytes = Mutex::new(random_seed_bytes);
+ Self { network_graph, logger, random_seed_bytes, scorer, score_params }
+ }
+}
+
+impl< G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref, SP: Sized, Sc: Score<ScoreParams = SP>> Router for DefaultRouter<G, L, S, SP, Sc> where
+ L::Target: Logger,
+ S::Target: for <'a> LockableScore<'a, Locked = MutexGuard<'a, Sc>>,
+{
+ fn find_route(
+ &self,
+ payer: &PublicKey,
+ params: &RouteParameters,
+ first_hops: Option<&[&ChannelDetails]>,
+ inflight_htlcs: &InFlightHtlcs
+ ) -> Result<Route, LightningError> {
+ let random_seed_bytes = {
+ let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
+ *locked_random_seed_bytes = Sha256::hash(&*locked_random_seed_bytes).into_inner();
+ *locked_random_seed_bytes
+ };
+ find_route(
+ payer, params, &self.network_graph, first_hops, &*self.logger,
+ &ScorerAccountingForInFlightHtlcs::new(self.scorer.lock(), inflight_htlcs),
+ &self.score_params,
+ &random_seed_bytes
+ )
+ }
+}
+
+/// A trait defining behavior for routing a payment.
+pub trait Router {
+ /// Finds a [`Route`] between `payer` and `payee` for a payment with the given values.
+ fn find_route(
+ &self, payer: &PublicKey, route_params: &RouteParameters,
+ first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: &InFlightHtlcs
+ ) -> Result<Route, LightningError>;
+ /// Finds a [`Route`] between `payer` and `payee` for a payment with the given values. Includes
+ /// `PaymentHash` and `PaymentId` to be able to correlate the request with a specific payment.
+ fn find_route_with_id(
+ &self, payer: &PublicKey, route_params: &RouteParameters,
+ first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: &InFlightHtlcs,
+ _payment_hash: PaymentHash, _payment_id: PaymentId
+ ) -> Result<Route, LightningError> {
+ self.find_route(payer, route_params, first_hops, inflight_htlcs)
+ }
+}
+
+/// [`Score`] implementation that factors in in-flight HTLC liquidity.
+///
+/// Useful for custom [`Router`] implementations to wrap their [`Score`] on-the-fly when calling
+/// [`find_route`].
+///
+/// [`Score`]: crate::routing::scoring::Score
+pub struct ScorerAccountingForInFlightHtlcs<'a, S: Score> {
+ scorer: S,
+ // Maps a channel's short channel id and its direction to the liquidity used up.
+ inflight_htlcs: &'a InFlightHtlcs,
+}
+
+impl<'a, S: Score> ScorerAccountingForInFlightHtlcs<'a, S> {
+ /// Initialize a new `ScorerAccountingForInFlightHtlcs`.
+ pub fn new(scorer: S, inflight_htlcs: &'a InFlightHtlcs) -> Self {
+ ScorerAccountingForInFlightHtlcs {
+ scorer,
+ inflight_htlcs
+ }
+ }
+}
+
+#[cfg(c_bindings)]
+impl<'a, S: Score> Writeable for ScorerAccountingForInFlightHtlcs<'a, S> {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> { self.scorer.write(writer) }
+}
+
+impl<'a, S: Score> Score for ScorerAccountingForInFlightHtlcs<'a, S> {
+ type ScoreParams = S::ScoreParams;
+ fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams) -> u64 {
+ if let Some(used_liquidity) = self.inflight_htlcs.used_liquidity_msat(
+ source, target, short_channel_id
+ ) {
+ let usage = ChannelUsage {
+ inflight_htlc_msat: usage.inflight_htlc_msat + used_liquidity,
+ ..usage
+ };
+
+ self.scorer.channel_penalty_msat(short_channel_id, source, target, usage, score_params)
+ } else {
+ self.scorer.channel_penalty_msat(short_channel_id, source, target, usage, score_params)
+ }
+ }
+
+ fn payment_path_failed(&mut self, path: &Path, short_channel_id: u64) {
+ self.scorer.payment_path_failed(path, short_channel_id)
+ }
+
+ fn payment_path_successful(&mut self, path: &Path) {
+ self.scorer.payment_path_successful(path)
+ }
+
+ fn probe_failed(&mut self, path: &Path, short_channel_id: u64) {
+ self.scorer.probe_failed(path, short_channel_id)
+ }
+
+ fn probe_successful(&mut self, path: &Path) {
+ self.scorer.probe_successful(path)
+ }
+}
+
+/// A data structure for tracking in-flight HTLCs. May be used during pathfinding to account for
+/// in-use channel liquidity.
+#[derive(Clone)]
+pub struct InFlightHtlcs(
+ // A map with liquidity value (in msat) keyed by a short channel id and the direction the HTLC
+ // is traveling in. The direction boolean is determined by checking if the HTLC source's public
+ // key is less than its destination. See `InFlightHtlcs::used_liquidity_msat` for more
+ // details.
+ HashMap<(u64, bool), u64>
+);