Merge pull request #1811 from valentinewallace/2022-10-chanman-router

Move `InflightHtlcs` and `Router` trait into `ChannelManager`
This commit is contained in:
Matt Corallo 2022-11-03 23:43:03 +00:00 committed by GitHub
commit e55e0d53c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 61 deletions

View file

@ -38,13 +38,13 @@
//! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure}; //! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
//! # use lightning::ln::msgs::LightningError; //! # use lightning::ln::msgs::LightningError;
//! # use lightning::routing::gossip::NodeId; //! # use lightning::routing::gossip::NodeId;
//! # use lightning::routing::router::{Route, RouteHop, RouteParameters}; //! # use lightning::routing::router::{InFlightHtlcs, Route, RouteHop, RouteParameters, Router};
//! # use lightning::routing::scoring::{ChannelUsage, Score}; //! # use lightning::routing::scoring::{ChannelUsage, Score};
//! # use lightning::util::events::{Event, EventHandler, EventsProvider}; //! # use lightning::util::events::{Event, EventHandler, EventsProvider};
//! # use lightning::util::logger::{Logger, Record}; //! # use lightning::util::logger::{Logger, Record};
//! # use lightning::util::ser::{Writeable, Writer}; //! # use lightning::util::ser::{Writeable, Writer};
//! # use lightning_invoice::Invoice; //! # use lightning_invoice::Invoice;
//! # use lightning_invoice::payment::{InFlightHtlcs, InvoicePayer, Payer, Retry, Router}; //! # use lightning_invoice::payment::{InvoicePayer, Payer, Retry, ScoringRouter};
//! # use secp256k1::PublicKey; //! # use secp256k1::PublicKey;
//! # use std::cell::RefCell; //! # use std::cell::RefCell;
//! # use std::ops::Deref; //! # use std::ops::Deref;
@ -74,10 +74,11 @@
//! # struct FakeRouter {} //! # struct FakeRouter {}
//! # impl Router for FakeRouter { //! # impl Router for FakeRouter {
//! # fn find_route( //! # fn find_route(
//! # &self, payer: &PublicKey, params: &RouteParameters, payment_hash: &PaymentHash, //! # &self, payer: &PublicKey, params: &RouteParameters,
//! # first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs //! # first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs
//! # ) -> Result<Route, LightningError> { unimplemented!() } //! # ) -> Result<Route, LightningError> { unimplemented!() }
//! # //! # }
//! # impl ScoringRouter for FakeRouter {
//! # fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) { unimplemented!() } //! # fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) { unimplemented!() }
//! # fn notify_payment_path_successful(&self, path: &[&RouteHop]) { unimplemented!() } //! # fn notify_payment_path_successful(&self, path: &[&RouteHop]) { unimplemented!() }
//! # fn notify_payment_probe_successful(&self, path: &[&RouteHop]) { unimplemented!() } //! # fn notify_payment_probe_successful(&self, path: &[&RouteHop]) { unimplemented!() }
@ -141,16 +142,14 @@ use bitcoin_hashes::Hash;
use bitcoin_hashes::sha256::Hash as Sha256; use bitcoin_hashes::sha256::Hash as Sha256;
use crate::prelude::*; use crate::prelude::*;
use lightning::io;
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure}; use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
use lightning::ln::msgs::LightningError; use lightning::ln::msgs::LightningError;
use lightning::routing::gossip::NodeId; use lightning::routing::gossip::NodeId;
use lightning::routing::router::{PaymentParameters, Route, RouteHop, RouteParameters}; use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters, Router};
use lightning::util::errors::APIError; use lightning::util::errors::APIError;
use lightning::util::events::{Event, EventHandler}; use lightning::util::events::{Event, EventHandler};
use lightning::util::logger::Logger; use lightning::util::logger::Logger;
use lightning::util::ser::Writeable;
use crate::time_utils::Time; use crate::time_utils::Time;
use crate::sync::Mutex; use crate::sync::Mutex;
@ -178,7 +177,7 @@ use crate::time_utils;
type ConfiguredTime = time_utils::Eternity; type ConfiguredTime = time_utils::Eternity;
/// (C-not exported) generally all users should use the [`InvoicePayer`] type alias. /// (C-not exported) generally all users should use the [`InvoicePayer`] type alias.
pub struct InvoicePayerUsingTime<P: Deref, R: Router, L: Deref, E: EventHandler, T: Time> pub struct InvoicePayerUsingTime<P: Deref, R: ScoringRouter, L: Deref, E: EventHandler, T: Time>
where where
P::Target: Payer, P::Target: Payer,
L::Target: Logger, L::Target: Logger,
@ -280,13 +279,20 @@ pub trait Payer {
fn abandon_payment(&self, payment_id: PaymentId); fn abandon_payment(&self, payment_id: PaymentId);
} }
/// A trait defining behavior for routing an [`Invoice`] payment. /// A trait defining behavior for a [`Router`] implementation that also supports scoring channels
pub trait Router { /// based on payment and probe success/failure.
/// Finds a [`Route`] between `payer` and `payee` for a payment with the given values. ///
fn find_route( /// [`Router`]: lightning::routing::router::Router
&self, payer: &PublicKey, route_params: &RouteParameters, payment_hash: &PaymentHash, pub trait ScoringRouter: Router {
first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs /// Finds a [`Route`] between `payer` and `payee` for a payment with the given values. Includes
) -> Result<Route, LightningError>; /// `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. /// Lets the router know that payment through a specific path has failed.
fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64); fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64);
/// Lets the router know that payment through a specific path was successful. /// Lets the router know that payment through a specific path was successful.
@ -336,7 +342,7 @@ pub enum PaymentError {
Sending(PaymentSendFailure), Sending(PaymentSendFailure),
} }
impl<P: Deref, R: Router, L: Deref, E: EventHandler, T: Time> InvoicePayerUsingTime<P, R, L, E, T> impl<P: Deref, R: ScoringRouter, L: Deref, E: EventHandler, T: Time> InvoicePayerUsingTime<P, R, L, E, T>
where where
P::Target: Payer, P::Target: Payer,
L::Target: Logger, L::Target: Logger,
@ -529,8 +535,7 @@ where
let first_hops = self.payer.first_hops(); let first_hops = self.payer.first_hops();
let inflight_htlcs = self.create_inflight_map(); let inflight_htlcs = self.create_inflight_map();
let route = self.router.find_route( let route = self.router.find_route(
&payer, &params, &payment_hash, Some(&first_hops.iter().collect::<Vec<_>>()), &payer, &params, Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs
inflight_htlcs
).map_err(|e| PaymentError::Routing(e))?; ).map_err(|e| PaymentError::Routing(e))?;
match send_payment(&route) { match send_payment(&route) {
@ -634,8 +639,7 @@ where
let inflight_htlcs = self.create_inflight_map(); let inflight_htlcs = self.create_inflight_map();
let route = self.router.find_route( let route = self.router.find_route(
&payer, &params, &payment_hash, Some(&first_hops.iter().collect::<Vec<_>>()), &payer, &params, Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs
inflight_htlcs
); );
if route.is_err() { if route.is_err() {
@ -688,9 +692,8 @@ where
self.payment_cache.lock().unwrap().remove(payment_hash); self.payment_cache.lock().unwrap().remove(payment_hash);
} }
/// Given a [`PaymentHash`], this function looks up inflight path attempts in the payment_cache. /// Use path information in the payment_cache to construct a HashMap mapping a channel's short
/// Then, it uses the path information inside the cache to construct a HashMap mapping a channel's /// channel id and direction to the amount being sent through it.
/// short channel id and direction to the amount being sent through it.
/// ///
/// This function should be called whenever we need information about currently used up liquidity /// This function should be called whenever we need information about currently used up liquidity
/// across payments. /// across payments.
@ -726,7 +729,7 @@ where
} }
} }
InFlightHtlcs(total_inflight_map) InFlightHtlcs::new(total_inflight_map)
} }
} }
@ -741,7 +744,7 @@ fn has_expired(route_params: &RouteParameters) -> bool {
} else { false } } else { false }
} }
impl<P: Deref, R: Router, L: Deref, E: EventHandler, T: Time> EventHandler for InvoicePayerUsingTime<P, R, L, E, T> impl<P: Deref, R: ScoringRouter, L: Deref, E: EventHandler, T: Time> EventHandler for InvoicePayerUsingTime<P, R, L, E, T>
where where
P::Target: Payer, P::Target: Payer,
L::Target: Logger, L::Target: Logger,
@ -815,31 +818,6 @@ where
} }
} }
/// 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.
pub struct InFlightHtlcs(HashMap<(u64, bool), u64>);
impl InFlightHtlcs {
/// Returns liquidity in msat given the public key of the HTLC source, target, and short channel
/// id.
pub fn used_liquidity_msat(&self, source: &NodeId, target: &NodeId, channel_scid: u64) -> Option<u64> {
self.0.get(&(channel_scid, source < target)).map(|v| *v)
}
}
impl Writeable for InFlightHtlcs {
fn write<W: lightning::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> { self.0.write(writer) }
}
impl lightning::util::ser::Readable for InFlightHtlcs {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, lightning::ln::msgs::DecodeError> {
let infight_map: HashMap<(u64, bool), u64> = lightning::util::ser::Readable::read(reader)?;
Ok(Self(infight_map))
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -852,7 +830,7 @@ mod tests {
use lightning::ln::functional_test_utils::*; use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs::{ChannelMessageHandler, ErrorAction, LightningError}; use lightning::ln::msgs::{ChannelMessageHandler, ErrorAction, LightningError};
use lightning::routing::gossip::{EffectiveCapacity, NodeId}; use lightning::routing::gossip::{EffectiveCapacity, NodeId};
use lightning::routing::router::{PaymentParameters, Route, RouteHop}; use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, Router};
use lightning::routing::scoring::{ChannelUsage, LockableScore, Score}; use lightning::routing::scoring::{ChannelUsage, LockableScore, Score};
use lightning::util::test_utils::TestLogger; use lightning::util::test_utils::TestLogger;
use lightning::util::errors::APIError; use lightning::util::errors::APIError;
@ -1895,7 +1873,7 @@ mod tests {
impl Router for TestRouter { impl Router for TestRouter {
fn find_route( fn find_route(
&self, payer: &PublicKey, route_params: &RouteParameters, _payment_hash: &PaymentHash, &self, payer: &PublicKey, route_params: &RouteParameters,
_first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs _first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError> { ) -> Result<Route, LightningError> {
// Simulate calling the Scorer just as you would in find_route // Simulate calling the Scorer just as you would in find_route
@ -1926,7 +1904,9 @@ mod tests {
payment_params: Some(route_params.payment_params.clone()), ..Self::route_for_value(route_params.final_value_msat) payment_params: Some(route_params.payment_params.clone()), ..Self::route_for_value(route_params.final_value_msat)
}) })
} }
}
impl ScoringRouter for TestRouter {
fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) { fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) {
self.scorer.lock().payment_path_failed(path, short_channel_id); self.scorer.lock().payment_path_failed(path, short_channel_id);
} }
@ -1948,12 +1928,14 @@ mod tests {
impl Router for FailingRouter { impl Router for FailingRouter {
fn find_route( fn find_route(
&self, _payer: &PublicKey, _params: &RouteParameters, _payment_hash: &PaymentHash, &self, _payer: &PublicKey, _params: &RouteParameters, _first_hops: Option<&[&ChannelDetails]>,
_first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs _inflight_htlcs: InFlightHtlcs,
) -> Result<Route, LightningError> { ) -> Result<Route, LightningError> {
Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError }) Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError })
} }
}
impl ScoringRouter for FailingRouter {
fn notify_payment_path_failed(&self, _path: &[&RouteHop], _short_channel_id: u64) {} fn notify_payment_path_failed(&self, _path: &[&RouteHop], _short_channel_id: u64) {}
fn notify_payment_path_successful(&self, _path: &[&RouteHop]) {} fn notify_payment_path_successful(&self, _path: &[&RouteHop]) {}
@ -2210,12 +2192,13 @@ mod tests {
impl Router for ManualRouter { impl Router for ManualRouter {
fn find_route( fn find_route(
&self, _payer: &PublicKey, _params: &RouteParameters, _payment_hash: &PaymentHash, &self, _payer: &PublicKey, _params: &RouteParameters, _first_hops: Option<&[&ChannelDetails]>,
_first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs _inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError> { ) -> Result<Route, LightningError> {
self.0.borrow_mut().pop_front().unwrap() self.0.borrow_mut().pop_front().unwrap()
} }
}
impl ScoringRouter for ManualRouter {
fn notify_payment_path_failed(&self, _path: &[&RouteHop], _short_channel_id: u64) {} fn notify_payment_path_failed(&self, _path: &[&RouteHop], _short_channel_id: u64) {}
fn notify_payment_path_successful(&self, _path: &[&RouteHop]) {} fn notify_payment_path_successful(&self, _path: &[&RouteHop]) {}

View file

@ -1,7 +1,7 @@
//! Convenient utilities to create an invoice. //! Convenient utilities to create an invoice.
use crate::{CreationError, Currency, Invoice, InvoiceBuilder, SignOrCreationError}; use crate::{CreationError, Currency, Invoice, InvoiceBuilder, SignOrCreationError};
use crate::payment::{InFlightHtlcs, Payer, Router}; use crate::payment::{Payer, ScoringRouter};
use crate::{prelude::*, Description, InvoiceDescription, Sha256}; use crate::{prelude::*, Description, InvoiceDescription, Sha256};
use bech32::ToBase32; use bech32::ToBase32;
@ -16,7 +16,7 @@ use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA};
use lightning::ln::inbound_payment::{create, create_from_hash, ExpandedKey}; use lightning::ln::inbound_payment::{create, create_from_hash, ExpandedKey};
use lightning::ln::msgs::LightningError; use lightning::ln::msgs::LightningError;
use lightning::routing::gossip::{NetworkGraph, NodeId, RoutingFees}; use lightning::routing::gossip::{NetworkGraph, NodeId, RoutingFees};
use lightning::routing::router::{Route, RouteHint, RouteHintHop, RouteParameters, find_route, RouteHop}; use lightning::routing::router::{InFlightHtlcs, Route, RouteHint, RouteHintHop, RouteParameters, find_route, RouteHop, Router};
use lightning::routing::scoring::{ChannelUsage, LockableScore, Score}; use lightning::routing::scoring::{ChannelUsage, LockableScore, Score};
use lightning::util::logger::Logger; use lightning::util::logger::Logger;
use secp256k1::PublicKey; use secp256k1::PublicKey;
@ -552,8 +552,8 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> Router for DefaultR
S::Target: for <'a> LockableScore<'a>, S::Target: for <'a> LockableScore<'a>,
{ {
fn find_route( fn find_route(
&self, payer: &PublicKey, params: &RouteParameters, _payment_hash: &PaymentHash, &self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>,
first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError> { ) -> Result<Route, LightningError> {
let random_seed_bytes = { let random_seed_bytes = {
let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap(); let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
@ -567,7 +567,12 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> Router for DefaultR
&random_seed_bytes &random_seed_bytes
) )
} }
}
impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> ScoringRouter for DefaultRouter<G, L, S> where
L::Target: Logger,
S::Target: for <'a> LockableScore<'a>,
{
fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) { fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) {
self.scorer.lock().payment_path_failed(path, short_channel_id); self.scorer.lock().payment_path_failed(path, short_channel_id);
} }

View file

@ -29,6 +29,49 @@ use alloc::collections::BinaryHeap;
use core::cmp; use core::cmp;
use core::ops::Deref; use core::ops::Deref;
/// 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>;
}
/// 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.
#[cfg(not(any(test, feature = "_test_utils")))]
pub struct InFlightHtlcs(HashMap<(u64, bool), u64>);
#[cfg(any(test, feature = "_test_utils"))]
pub struct InFlightHtlcs(pub HashMap<(u64, bool), u64>);
impl InFlightHtlcs {
/// Create a new `InFlightHtlcs` via a mapping from:
/// (short_channel_id, source_pubkey < target_pubkey) -> used_liquidity_msat
pub fn new(inflight_map: HashMap<(u64, bool), u64>) -> Self {
InFlightHtlcs(inflight_map)
}
/// Returns liquidity in msat given the public key of the HTLC source, target, and short channel
/// id.
pub fn used_liquidity_msat(&self, source: &NodeId, target: &NodeId, channel_scid: u64) -> Option<u64> {
self.0.get(&(channel_scid, source < target)).map(|v| *v)
}
}
impl Writeable for InFlightHtlcs {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> { self.0.write(writer) }
}
impl Readable for InFlightHtlcs {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
let infight_map: HashMap<(u64, bool), u64> = Readable::read(reader)?;
Ok(Self(infight_map))
}
}
/// A hop in a route /// A hop in a route
#[derive(Clone, Debug, Hash, PartialEq, Eq)] #[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct RouteHop { pub struct RouteHop {