Merge pull request #1862 from valentinewallace/2022-11-chanman-retries-prep
[rust-lightning] / lightning / src / routing / router.rs
index 12306ac92ad13d68f4ee0be0f4c72b3b2161509a..09cee2a2e84e8b8acb2d63842c2ed054d387e9ee 100644 (file)
 //! interrogate it to get routes for your own payments.
 
 use bitcoin::secp256k1::PublicKey;
+use bitcoin::hashes::Hash;
+use bitcoin::hashes::sha256::Hash as Sha256;
 
-use crate::ln::channelmanager::ChannelDetails;
+use crate::ln::PaymentHash;
+use crate::ln::channelmanager::{ChannelDetails, PaymentId};
 use crate::ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures};
 use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
 use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
-use crate::routing::scoring::{ChannelUsage, Score};
+use crate::routing::scoring::{ChannelUsage, LockableScore, Score};
 use crate::util::ser::{Writeable, Readable, Writer};
 use crate::util::logger::{Level, Logger};
 use crate::util::chacha20::ChaCha20;
 
 use crate::io;
 use crate::prelude::*;
+use crate::sync::Mutex;
 use alloc::collections::BinaryHeap;
 use core::cmp;
 use core::ops::Deref;
 
+/// A [`Router`] implemented using [`find_route`].
+pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> where
+       L::Target: Logger,
+       S::Target: for <'a> LockableScore<'a>,
+{
+       network_graph: G,
+       logger: L,
+       random_seed_bytes: Mutex<[u8; 32]>,
+       scorer: S
+}
+
+impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> DefaultRouter<G, L, S> where
+       L::Target: Logger,
+       S::Target: for <'a> LockableScore<'a>,
+{
+       /// Creates a new router.
+       pub fn new(network_graph: G, logger: L, random_seed_bytes: [u8; 32], scorer: S) -> Self {
+               let random_seed_bytes = Mutex::new(random_seed_bytes);
+               Self { network_graph, logger, random_seed_bytes, scorer }
+       }
+}
+
+impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> Router for DefaultRouter<G, L, S> where
+       L::Target: Logger,
+       S::Target: for <'a> LockableScore<'a>,
+{
+       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(&mut self.scorer.lock(), inflight_htlcs),
+                       &random_seed_bytes
+               )
+       }
+
+       fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) {
+               self.scorer.lock().payment_path_failed(path, short_channel_id);
+       }
+
+       fn notify_payment_path_successful(&self, path: &[&RouteHop]) {
+               self.scorer.lock().payment_path_successful(path);
+       }
+
+       fn notify_payment_probe_successful(&self, path: &[&RouteHop]) {
+               self.scorer.lock().probe_successful(path);
+       }
+
+       fn notify_payment_probe_failed(&self, path: &[&RouteHop], short_channel_id: u64) {
+               self.scorer.lock().probe_failed(path, short_channel_id);
+       }
+}
+
 /// 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.
@@ -36,6 +100,83 @@ pub trait Router {
                &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)
+       }
+       /// Lets the router know that payment through a specific path has failed.
+       fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64);
+       /// Lets the router know that payment through a specific path was successful.
+       fn notify_payment_path_successful(&self, path: &[&RouteHop]);
+       /// Lets the router know that a payment probe was successful.
+       fn notify_payment_probe_successful(&self, path: &[&RouteHop]);
+       /// Lets the router know that a payment probe failed.
+       fn notify_payment_probe_failed(&self, path: &[&RouteHop], short_channel_id: u64);
+}
+
+/// [`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: &'a mut S,
+       // Maps a channel's short channel id and its direction to the liquidity used up.
+       inflight_htlcs: InFlightHtlcs,
+}
+
+impl<'a, S: Score> ScorerAccountingForInFlightHtlcs<'a, S> {
+       /// Initialize a new `ScorerAccountingForInFlightHtlcs`.
+       pub fn new(scorer: &'a mut S, inflight_htlcs: 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> {
+       fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage) -> 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)
+               } else {
+                       self.scorer.channel_penalty_msat(short_channel_id, source, target, usage)
+               }
+       }
+
+       fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
+               self.scorer.payment_path_failed(path, short_channel_id)
+       }
+
+       fn payment_path_successful(&mut self, path: &[&RouteHop]) {
+               self.scorer.payment_path_successful(path)
+       }
+
+       fn probe_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
+               self.scorer.probe_failed(path, short_channel_id)
+       }
+
+       fn probe_successful(&mut self, path: &[&RouteHop]) {
+               self.scorer.probe_successful(path)
+       }
 }
 
 /// A data structure for tracking in-flight HTLCs. May be used during pathfinding to account for