Merge pull request #1694 from jurvis/jurvis/2022-08-move-scorer-from-router

Move `LockableScore` requirement away from `Router` trait
This commit is contained in:
Jeffrey Czyz 2022-09-16 14:01:38 -05:00 committed by GitHub
commit ca76d0675b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 282 additions and 206 deletions

View file

@ -1117,8 +1117,8 @@ mod tests {
// Initiate the background processors to watch each node.
let data_dir = nodes[0].persister.get_data_dir();
let persister = Arc::new(Persister::new(data_dir));
let router = DefaultRouter::new(Arc::clone(&nodes[0].network_graph), Arc::clone(&nodes[0].logger), random_seed_bytes);
let invoice_payer = Arc::new(InvoicePayer::new(Arc::clone(&nodes[0].node), router, Arc::clone(&nodes[0].scorer), Arc::clone(&nodes[0].logger), |_: &_| {}, Retry::Attempts(2)));
let router = DefaultRouter::new(Arc::clone(&nodes[0].network_graph), Arc::clone(&nodes[0].logger), random_seed_bytes, Arc::clone(&nodes[0].scorer));
let invoice_payer = Arc::new(InvoicePayer::new(Arc::clone(&nodes[0].node), router, Arc::clone(&nodes[0].logger), |_: &_| {}, Retry::Attempts(2)));
let event_handler = Arc::clone(&invoice_payer);
let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].no_gossip_sync(), nodes[0].peer_manager.clone(), nodes[0].logger.clone(), Some(nodes[0].scorer.clone()));
assert!(bg_processor.stop().is_ok());

View file

@ -15,7 +15,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[features]
default = ["std"]
no-std = ["hashbrown", "lightning/no-std", "core2/alloc"]
no-std = ["hashbrown", "lightning/no-std"]
std = ["bitcoin_hashes/std", "num-traits/std", "lightning/std", "bech32/std"]
[dependencies]
@ -25,7 +25,6 @@ secp256k1 = { version = "0.24.0", default-features = false, features = ["recover
num-traits = { version = "0.2.8", default-features = false }
bitcoin_hashes = { version = "0.11", default-features = false }
hashbrown = { version = "0.11", optional = true }
core2 = { version = "0.3.0", default-features = false, optional = true }
serde = { version = "1.0.118", optional = true }
[dev-dependencies]

View file

@ -15,9 +15,10 @@
//! and payee using information provided by the payer and from the payee's [`Invoice`], when
//! applicable.
//!
//! [`InvoicePayer`] is parameterized by a [`LockableScore`], which it uses for scoring failed and
//! successful payment paths upon receiving [`Event::PaymentPathFailed`] and
//! [`Event::PaymentPathSuccessful`] events, respectively.
//! [`InvoicePayer`] uses its [`Router`] parameterization for optionally notifying scorers upon
//! receiving the [`Event::PaymentPathFailed`] and [`Event::PaymentPathSuccessful`] events.
//! It also does the same for payment probe failure and success events using [`Event::ProbeFailed`]
//! and [`Event::ProbeSuccessful`].
//!
//! [`InvoicePayer`] is capable of retrying failed payments. It accomplishes this by implementing
//! [`EventHandler`] which decorates a user-provided handler. It will intercept any
@ -32,9 +33,7 @@
//! # extern crate lightning_invoice;
//! # extern crate secp256k1;
//! #
//! # #[cfg(feature = "no-std")]
//! # extern crate core2;
//! #
//! # use lightning::io;
//! # use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
//! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
//! # use lightning::ln::msgs::LightningError;
@ -45,16 +44,11 @@
//! # use lightning::util::logger::{Logger, Record};
//! # use lightning::util::ser::{Writeable, Writer};
//! # use lightning_invoice::Invoice;
//! # use lightning_invoice::payment::{InvoicePayer, Payer, Retry, Router};
//! # use lightning_invoice::payment::{InFlightHtlcs, InvoicePayer, Payer, Retry, Router};
//! # use secp256k1::PublicKey;
//! # use std::cell::RefCell;
//! # use std::ops::Deref;
//! #
//! # #[cfg(not(feature = "std"))]
//! # use core2::io;
//! # #[cfg(feature = "std")]
//! # use std::io;
//! #
//! # struct FakeEventProvider {}
//! # impl EventsProvider for FakeEventProvider {
//! # fn process_pending_events<H: Deref>(&self, handler: H) where H::Target: EventHandler {}
@ -78,10 +72,15 @@
//! #
//! # struct FakeRouter {}
//! # impl Router for FakeRouter {
//! # fn find_route<S: Score>(
//! # fn find_route(
//! # &self, payer: &PublicKey, params: &RouteParameters, payment_hash: &PaymentHash,
//! # first_hops: Option<&[&ChannelDetails]>, scorer: &S
//! # first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs
//! # ) -> Result<Route, LightningError> { unimplemented!() }
//! #
//! # fn notify_payment_path_failed(&self, path: Vec<&RouteHop>, short_channel_id: u64) { unimplemented!() }
//! # fn notify_payment_path_successful(&self, path: Vec<&RouteHop>) { unimplemented!() }
//! # fn notify_payment_probe_successful(&self, path: Vec<&RouteHop>) { unimplemented!() }
//! # fn notify_payment_probe_failed(&self, path: Vec<&RouteHop>, short_channel_id: u64) { unimplemented!() }
//! # }
//! #
//! # struct FakeScorer {}
@ -115,7 +114,7 @@
//! # let router = FakeRouter {};
//! # let scorer = RefCell::new(FakeScorer {});
//! # let logger = FakeLogger {};
//! let invoice_payer = InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
//! let invoice_payer = InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
//!
//! let invoice = "...";
//! if let Ok(invoice) = invoice.parse::<Invoice>() {
@ -141,11 +140,11 @@ use bitcoin_hashes::Hash;
use bitcoin_hashes::sha256::Hash as Sha256;
use crate::prelude::*;
use lightning::io;
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
use lightning::ln::msgs::LightningError;
use lightning::routing::gossip::NodeId;
use lightning::routing::scoring::{ChannelUsage, LockableScore, Score};
use lightning::routing::router::{PaymentParameters, Route, RouteHop, RouteParameters};
use lightning::util::errors::APIError;
use lightning::util::events::{Event, EventHandler};
@ -167,7 +166,7 @@ use std::time::SystemTime;
/// See [module-level documentation] for details.
///
/// [module-level documentation]: crate::payment
pub type InvoicePayer<P, R, S, L, E> = InvoicePayerUsingTime::<P, R, S, L, E, ConfiguredTime>;
pub type InvoicePayer<P, R, L, E> = InvoicePayerUsingTime::<P, R, L, E, ConfiguredTime>;
#[cfg(not(feature = "no-std"))]
type ConfiguredTime = std::time::Instant;
@ -177,15 +176,13 @@ use time_utils;
type ConfiguredTime = time_utils::Eternity;
/// (C-not exported) generally all users should use the [`InvoicePayer`] type alias.
pub struct InvoicePayerUsingTime<P: Deref, R: Router, S: Deref, L: Deref, E: EventHandler, T: Time>
pub struct InvoicePayerUsingTime<P: Deref, R: Router, L: Deref, E: EventHandler, T: Time>
where
P::Target: Payer,
S::Target: for <'a> LockableScore<'a>,
L::Target: Logger,
{
payer: P,
router: R,
scorer: S,
logger: L,
event_handler: E,
/// Caches the overall attempts at making a payment, which is updated prior to retrying.
@ -209,49 +206,6 @@ impl<T: Time> PaymentInfo<T> {
}
}
/// Used to store information about all the HTLCs that are inflight across all payment attempts
struct AccountForInFlightHtlcs<'a, S: Score> {
scorer: &'a mut S,
/// Maps a channel's short channel id and its direction to the liquidity used up.
inflight_htlcs: HashMap<(u64, bool), u64>,
}
#[cfg(c_bindings)]
impl<'a, S:Score> lightning::util::ser::Writeable for AccountForInFlightHtlcs<'a, S> {
fn write<W: lightning::util::ser::Writer>(&self, writer: &mut W) -> Result<(), std::io::Error> { self.scorer.write(writer) }
}
impl<'a, S: Score> Score for AccountForInFlightHtlcs<'a, S> {
fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage) -> u64 {
if let Some(used_liqudity) = self.inflight_htlcs.get(&(short_channel_id, source < target)) {
let usage = ChannelUsage {
inflight_htlc_msat: usage.inflight_htlc_msat + used_liqudity,
..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)
}
}
/// Storing minimal payment attempts information required for determining if a outbound payment can
/// be retried.
#[derive(Clone, Copy)]
@ -314,10 +268,18 @@ pub trait Payer {
/// A trait defining behavior for routing an [`Invoice`] payment.
pub trait Router {
/// Finds a [`Route`] between `payer` and `payee` for a payment with the given values.
fn find_route<S: Score>(
fn find_route(
&self, payer: &PublicKey, route_params: &RouteParameters, payment_hash: &PaymentHash,
first_hops: Option<&[&ChannelDetails]>, scorer: &S
first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError>;
/// Lets the router know that payment through a specific path has failed.
fn notify_payment_path_failed(&self, path: Vec<&RouteHop>, short_channel_id: u64);
/// Lets the router know that payment through a specific path was successful.
fn notify_payment_path_successful(&self, path: Vec<&RouteHop>);
/// Lets the router know that a payment probe was successful.
fn notify_payment_probe_successful(&self, path: Vec<&RouteHop>);
/// Lets the router know that a payment probe failed.
fn notify_payment_probe_failed(&self, path: Vec<&RouteHop>, short_channel_id: u64);
}
/// Strategies available to retry payment path failures for an [`Invoice`].
@ -359,10 +321,9 @@ pub enum PaymentError {
Sending(PaymentSendFailure),
}
impl<P: Deref, R: Router, S: Deref, L: Deref, E: EventHandler, T: Time> InvoicePayerUsingTime<P, R, S, L, E, T>
impl<P: Deref, R: Router, L: Deref, E: EventHandler, T: Time> InvoicePayerUsingTime<P, R, L, E, T>
where
P::Target: Payer,
S::Target: for <'a> LockableScore<'a>,
L::Target: Logger,
{
/// Creates an invoice payer that retries failed payment paths.
@ -370,12 +331,11 @@ where
/// Will forward any [`Event::PaymentPathFailed`] events to the decorated `event_handler` once
/// `retry` has been exceeded for a given [`Invoice`].
pub fn new(
payer: P, router: R, scorer: S, logger: L, event_handler: E, retry: Retry
payer: P, router: R, logger: L, event_handler: E, retry: Retry
) -> Self {
Self {
payer,
router,
scorer,
logger,
event_handler,
payment_cache: Mutex::new(HashMap::new()),
@ -487,7 +447,7 @@ where
let inflight_htlcs = self.create_inflight_map();
let route = self.router.find_route(
&payer, &params, &payment_hash, Some(&first_hops.iter().collect::<Vec<_>>()),
&AccountForInFlightHtlcs { scorer: &mut self.scorer.lock(), inflight_htlcs }
inflight_htlcs
).map_err(|e| PaymentError::Routing(e))?;
match send_payment(&route) {
@ -592,7 +552,7 @@ where
let route = self.router.find_route(
&payer, &params, &payment_hash, Some(&first_hops.iter().collect::<Vec<_>>()),
&AccountForInFlightHtlcs { scorer: &mut self.scorer.lock(), inflight_htlcs }
inflight_htlcs
);
if route.is_err() {
@ -651,7 +611,7 @@ where
///
/// This function should be called whenever we need information about currently used up liquidity
/// across payments.
fn create_inflight_map(&self) -> HashMap<(u64, bool), u64> {
fn create_inflight_map(&self) -> InFlightHtlcs {
let mut total_inflight_map: HashMap<(u64, bool), u64> = HashMap::new();
// Make an attempt at finding existing payment information from `payment_cache`. If it
// does not exist, it probably is a fresh payment and we can just return an empty
@ -683,7 +643,7 @@ where
}
}
total_inflight_map
InFlightHtlcs(total_inflight_map)
}
}
@ -698,10 +658,9 @@ fn has_expired(route_params: &RouteParameters) -> bool {
} else { false }
}
impl<P: Deref, R: Router, S: Deref, L: Deref, E: EventHandler, T: Time> EventHandler for InvoicePayerUsingTime<P, R, S, L, E, T>
impl<P: Deref, R: Router, L: Deref, E: EventHandler, T: Time> EventHandler for InvoicePayerUsingTime<P, R, L, E, T>
where
P::Target: Payer,
S::Target: for <'a> LockableScore<'a>,
L::Target: Logger,
{
fn handle_event(&self, event: &Event) {
@ -721,7 +680,7 @@ where
} => {
if let Some(short_channel_id) = short_channel_id {
let path = path.iter().collect::<Vec<_>>();
self.scorer.lock().payment_path_failed(&path, *short_channel_id);
self.router.notify_payment_path_failed(path, *short_channel_id)
}
if payment_id.is_none() {
@ -744,7 +703,7 @@ where
},
Event::PaymentPathSuccessful { path, .. } => {
let path = path.iter().collect::<Vec<_>>();
self.scorer.lock().payment_path_successful(&path);
self.router.notify_payment_path_successful(path);
},
Event::PaymentSent { payment_hash, .. } => {
let mut payment_cache = self.payment_cache.lock().unwrap();
@ -756,13 +715,13 @@ where
Event::ProbeSuccessful { payment_hash, path, .. } => {
log_trace!(self.logger, "Probe payment {} of {}msat was successful", log_bytes!(payment_hash.0), path.last().unwrap().fee_msat);
let path = path.iter().collect::<Vec<_>>();
self.scorer.lock().probe_successful(&path);
self.router.notify_payment_probe_successful(path);
},
Event::ProbeFailed { payment_hash, path, short_channel_id, .. } => {
if let Some(short_channel_id) = short_channel_id {
log_trace!(self.logger, "Probe payment {} of {}msat failed at channel {}", log_bytes!(payment_hash.0), path.last().unwrap().fee_msat, *short_channel_id);
let path = path.iter().collect::<Vec<_>>();
self.scorer.lock().probe_failed(&path, *short_channel_id);
self.router.notify_payment_probe_failed(path, *short_channel_id);
}
},
_ => {},
@ -773,11 +732,36 @@ 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))
}
}
impl lightning::util::ser::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)]
mod tests {
use super::*;
use crate::{InvoiceBuilder, Currency};
use utils::create_invoice_from_channelmanager_and_duration_since_epoch;
use utils::{ScorerAccountingForInFlightHtlcs, create_invoice_from_channelmanager_and_duration_since_epoch};
use bitcoin_hashes::sha256::Hash as Sha256;
use lightning::ln::PaymentPreimage;
use lightning::ln::channelmanager;
@ -786,13 +770,14 @@ mod tests {
use lightning::ln::msgs::{ChannelMessageHandler, ErrorAction, LightningError};
use lightning::routing::gossip::{EffectiveCapacity, NodeId};
use lightning::routing::router::{PaymentParameters, Route, RouteHop};
use lightning::routing::scoring::ChannelUsage;
use lightning::routing::scoring::{ChannelUsage, LockableScore, Score};
use lightning::util::test_utils::TestLogger;
use lightning::util::errors::APIError;
use lightning::util::events::{Event, EventsProvider, MessageSendEvent, MessageSendEventsProvider};
use secp256k1::{SecretKey, PublicKey, Secp256k1};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::ops::DerefMut;
use std::time::{SystemTime, Duration};
use time_utils::tests::SinceEpoch;
use DEFAULT_EXPIRY_TIME;
@ -875,11 +860,10 @@ mod tests {
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(0));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
assert_eq!(*payer.attempts.borrow(), 1);
@ -904,11 +888,10 @@ mod tests {
let payer = TestPayer::new()
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
assert_eq!(*payer.attempts.borrow(), 1);
@ -949,11 +932,10 @@ mod tests {
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat / 2))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
assert!(invoice_payer.pay_invoice(&invoice).is_ok());
}
@ -971,11 +953,10 @@ mod tests {
let payer = TestPayer::new()
.expect_send(Amount::OnRetry(final_value_msat / 2))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(PaymentId([1; 32]));
let event = Event::PaymentPathFailed {
@ -1016,11 +997,10 @@ mod tests {
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat / 2))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
assert_eq!(*payer.attempts.borrow(), 1);
@ -1074,13 +1054,12 @@ mod tests {
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
type InvoicePayerUsingSinceEpoch <P, R, S, L, E> = InvoicePayerUsingTime::<P, R, S, L, E, SinceEpoch>;
type InvoicePayerUsingSinceEpoch <P, R, L, E> = InvoicePayerUsingTime::<P, R, L, E, SinceEpoch>;
let invoice_payer =
InvoicePayerUsingSinceEpoch::new(&payer, router, &scorer, &logger, event_handler, Retry::Timeout(Duration::from_secs(120)));
InvoicePayerUsingSinceEpoch::new(&payer, router, &logger, event_handler, Retry::Timeout(Duration::from_secs(120)));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
assert_eq!(*payer.attempts.borrow(), 1);
@ -1116,11 +1095,10 @@ mod tests {
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
assert_eq!(*payer.attempts.borrow(), 1);
@ -1148,11 +1126,10 @@ mod tests {
let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
let payer = TestPayer::new();
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_preimage = PaymentPreimage([1; 32]);
let invoice = expired_invoice(payment_preimage);
@ -1173,11 +1150,10 @@ mod tests {
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
assert_eq!(*payer.attempts.borrow(), 1);
@ -1214,11 +1190,10 @@ mod tests {
.fails_on_attempt(2)
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
assert_eq!(*payer.attempts.borrow(), 1);
@ -1248,11 +1223,10 @@ mod tests {
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
assert_eq!(*payer.attempts.borrow(), 1);
@ -1284,11 +1258,10 @@ mod tests {
let payer = TestPayer::new()
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(0));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
@ -1324,10 +1297,9 @@ mod tests {
fn fails_paying_invoice_with_routing_errors() {
let payer = TestPayer::new();
let router = FailingRouter {};
let scorer = RefCell::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, |_: &_| {}, Retry::Attempts(0));
InvoicePayer::new(&payer, router, &logger, |_: &_| {}, Retry::Attempts(0));
let payment_preimage = PaymentPreimage([1; 32]);
let invoice = invoice(payment_preimage);
@ -1347,11 +1319,10 @@ mod tests {
let payer = TestPayer::new()
.fails_on_attempt(1)
.expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, |_: &_| {}, Retry::Attempts(0));
InvoicePayer::new(&payer, router, &logger, |_: &_| {}, Retry::Attempts(0));
match invoice_payer.pay_invoice(&invoice) {
Err(PaymentError::Sending(_)) => {},
@ -1371,11 +1342,10 @@ mod tests {
let final_value_msat = 100;
let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(0));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
let payment_id =
Some(invoice_payer.pay_zero_value_invoice(&invoice, final_value_msat).unwrap());
@ -1394,11 +1364,10 @@ mod tests {
let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
let payer = TestPayer::new();
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(0));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
let payment_preimage = PaymentPreimage([1; 32]);
let invoice = invoice(payment_preimage);
@ -1425,11 +1394,10 @@ mod tests {
let payer = TestPayer::new()
.expect_send(Amount::Spontaneous(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(invoice_payer.pay_pubkey(
pubkey, payment_preimage, final_value_msat, final_cltv_expiry_delta
@ -1478,13 +1446,13 @@ mod tests {
let payer = TestPayer::new()
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new().expect(TestResult::PaymentFailure {
let scorer = TestScorer::new().expect(TestResult::PaymentFailure {
path: path.clone(), short_channel_id: path[0].short_channel_id,
}));
});
let router = TestRouter::new(scorer);
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
let event = Event::PaymentPathFailed {
@ -1513,14 +1481,13 @@ mod tests {
// Expect that scorer is given short_channel_id upon handling the event.
let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new()
let scorer = TestScorer::new()
.expect(TestResult::PaymentSuccess { path: route.paths[0].clone() })
.expect(TestResult::PaymentSuccess { path: route.paths[1].clone() })
);
.expect(TestResult::PaymentSuccess { path: route.paths[1].clone() });
let router = TestRouter::new(scorer);
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = invoice_payer.pay_invoice(&invoice).unwrap();
let event = Event::PaymentPathSuccessful {
@ -1546,23 +1513,22 @@ mod tests {
let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let route = TestRouter::route_for_value(final_value_msat);
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(0));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
let payment_id = invoice_payer.pay_invoice(&invoice).unwrap();
let inflight_map = invoice_payer.create_inflight_map();
// First path check
assert_eq!(inflight_map.get(&(0, false)).unwrap().clone(), 94);
assert_eq!(inflight_map.get(&(1, true)).unwrap().clone(), 84);
assert_eq!(inflight_map.get(&(2, false)).unwrap().clone(), 64);
assert_eq!(inflight_map.0.get(&(0, false)).unwrap().clone(), 94);
assert_eq!(inflight_map.0.get(&(1, true)).unwrap().clone(), 84);
assert_eq!(inflight_map.0.get(&(2, false)).unwrap().clone(), 64);
// Second path check
assert_eq!(inflight_map.get(&(3, false)).unwrap().clone(), 74);
assert_eq!(inflight_map.get(&(4, false)).unwrap().clone(), 64);
assert_eq!(inflight_map.0.get(&(3, false)).unwrap().clone(), 74);
assert_eq!(inflight_map.0.get(&(4, false)).unwrap().clone(), 64);
invoice_payer.handle_event(&Event::PaymentPathSuccessful {
payment_id, payment_hash, path: route.paths[0].clone()
@ -1570,13 +1536,13 @@ mod tests {
let inflight_map = invoice_payer.create_inflight_map();
assert_eq!(inflight_map.get(&(0, false)), None);
assert_eq!(inflight_map.get(&(1, true)), None);
assert_eq!(inflight_map.get(&(2, false)), None);
assert_eq!(inflight_map.0.get(&(0, false)), None);
assert_eq!(inflight_map.0.get(&(1, true)), None);
assert_eq!(inflight_map.0.get(&(2, false)), None);
// Second path should still be inflight
assert_eq!(inflight_map.get(&(3, false)).unwrap().clone(), 74);
assert_eq!(inflight_map.get(&(4, false)).unwrap().clone(), 64)
assert_eq!(inflight_map.0.get(&(3, false)).unwrap().clone(), 74);
assert_eq!(inflight_map.0.get(&(4, false)).unwrap().clone(), 64)
}
#[test]
@ -1595,8 +1561,7 @@ mod tests {
.expect_send(Amount::ForInvoice(final_value_msat));
let final_value_msat = payment_invoice.amount_milli_satoshis().unwrap();
let route = TestRouter::route_for_value(final_value_msat);
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new()
let scorer = TestScorer::new()
// 1st invoice, 1st path
.expect_usage(ChannelUsage { amount_msat: 64, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
.expect_usage(ChannelUsage { amount_msat: 84, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
@ -1610,11 +1575,11 @@ mod tests {
.expect_usage(ChannelUsage { amount_msat: 94, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
// 2nd invoice, 2nd path
.expect_usage(ChannelUsage { amount_msat: 64, inflight_htlc_msat: 64, effective_capacity: EffectiveCapacity::Unknown } )
.expect_usage(ChannelUsage { amount_msat: 74, inflight_htlc_msat: 74, effective_capacity: EffectiveCapacity::Unknown } )
);
.expect_usage(ChannelUsage { amount_msat: 74, inflight_htlc_msat: 74, effective_capacity: EffectiveCapacity::Unknown } );
let router = TestRouter::new(scorer);
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(0));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
// Succeed 1st path, leave 2nd path inflight
let payment_id = invoice_payer.pay_invoice(&payment_invoice).unwrap();
@ -1647,8 +1612,7 @@ mod tests {
.expect_send(Amount::OnRetry(final_value_msat / 2))
.expect_send(Amount::OnRetry(final_value_msat / 4));
let final_value_msat = payment_invoice.amount_milli_satoshis().unwrap();
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new()
let scorer = TestScorer::new()
// 1st invoice, 1st path
.expect_usage(ChannelUsage { amount_msat: 64, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
.expect_usage(ChannelUsage { amount_msat: 84, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
@ -1669,11 +1633,11 @@ mod tests {
.expect_usage(ChannelUsage { amount_msat: 46, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown } )
// Retry 2, 2nd path
.expect_usage(ChannelUsage { amount_msat: 16, inflight_htlc_msat: 64 + 32, effective_capacity: EffectiveCapacity::Unknown } )
.expect_usage(ChannelUsage { amount_msat: 26, inflight_htlc_msat: 74 + 32 + 10, effective_capacity: EffectiveCapacity::Unknown } )
);
.expect_usage(ChannelUsage { amount_msat: 26, inflight_htlc_msat: 74 + 32 + 10, effective_capacity: EffectiveCapacity::Unknown } );
let router = TestRouter::new(scorer);
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(2));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
// Fail 1st path, leave 2nd path inflight
let payment_id = Some(invoice_payer.pay_invoice(&payment_invoice).unwrap());
@ -1722,18 +1686,17 @@ mod tests {
]))
.expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(0));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
invoice_payer.pay_invoice(&invoice_to_pay).unwrap();
let inflight_map = invoice_payer.create_inflight_map();
// Only the second path, which failed with `MonitorUpdateFailed` should be added to our
// inflight map because retries are disabled.
assert_eq!(inflight_map.len(), 2);
assert_eq!(inflight_map.0.len(), 2);
}
#[test]
@ -1754,22 +1717,27 @@ mod tests {
]))
.expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new());
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &scorer, &logger, event_handler, Retry::Attempts(0));
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
invoice_payer.pay_invoice(&invoice_to_pay).unwrap();
let inflight_map = invoice_payer.create_inflight_map();
// All paths successful, hence we check of the existence of all 5 hops.
assert_eq!(inflight_map.len(), 5);
assert_eq!(inflight_map.0.len(), 5);
}
struct TestRouter;
struct TestRouter {
scorer: RefCell<TestScorer>,
}
impl TestRouter {
fn new(scorer: TestScorer) -> Self {
TestRouter { scorer: RefCell::new(scorer) }
}
fn route_for_value(final_value_msat: u64) -> Route {
Route {
paths: vec![
@ -1843,12 +1811,14 @@ mod tests {
}
impl Router for TestRouter {
fn find_route<S: Score>(
fn find_route(
&self, payer: &PublicKey, route_params: &RouteParameters, _payment_hash: &PaymentHash,
_first_hops: Option<&[&ChannelDetails]>, scorer: &S
_first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError> {
// Simulate calling the Scorer just as you would in find_route
let route = Self::route_for_value(route_params.final_value_msat);
let mut locked_scorer = self.scorer.lock();
let scorer = ScorerAccountingForInFlightHtlcs::new(locked_scorer.deref_mut(), inflight_htlcs);
for path in route.paths {
let mut aggregate_msat = 0u64;
for (idx, hop) in path.iter().rev().enumerate() {
@ -1873,17 +1843,41 @@ mod tests {
payment_params: Some(route_params.payment_params.clone()), ..Self::route_for_value(route_params.final_value_msat)
})
}
fn notify_payment_path_failed(&self, path: Vec<&RouteHop>, short_channel_id: u64) {
self.scorer.lock().payment_path_failed(&path, short_channel_id);
}
fn notify_payment_path_successful(&self, path: Vec<&RouteHop>) {
self.scorer.lock().payment_path_successful(&path);
}
fn notify_payment_probe_successful(&self, path: Vec<&RouteHop>) {
self.scorer.lock().probe_successful(&path);
}
fn notify_payment_probe_failed(&self, path: Vec<&RouteHop>, short_channel_id: u64) {
self.scorer.lock().probe_failed(&path, short_channel_id);
}
}
struct FailingRouter;
impl Router for FailingRouter {
fn find_route<S: Score>(
fn find_route(
&self, _payer: &PublicKey, _params: &RouteParameters, _payment_hash: &PaymentHash,
_first_hops: Option<&[&ChannelDetails]>, _scorer: &S
_first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError> {
Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError })
}
fn notify_payment_path_failed(&self, _path: Vec<&RouteHop>, _short_channel_id: u64) {}
fn notify_payment_path_successful(&self, _path: Vec<&RouteHop>) {}
fn notify_payment_probe_successful(&self, _path: Vec<&RouteHop>) {}
fn notify_payment_probe_failed(&self, _path: Vec<&RouteHop>, _short_channel_id: u64) {}
}
struct TestScorer {
@ -1947,7 +1941,7 @@ mod tests {
Some(TestResult::PaymentSuccess { path }) => {
panic!("Unexpected successful payment path: {:?}", path)
},
None => panic!("Unexpected payment_path_failed call: {:?}", actual_path),
None => panic!("Unexpected notify_payment_path_failed call: {:?}", actual_path),
}
}
}
@ -1961,7 +1955,7 @@ mod tests {
Some(TestResult::PaymentSuccess { path }) => {
assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
},
None => panic!("Unexpected payment_path_successful call: {:?}", actual_path),
None => panic!("Unexpected notify_payment_path_successful call: {:?}", actual_path),
}
}
}
@ -1975,7 +1969,7 @@ mod tests {
Some(TestResult::PaymentSuccess { path }) => {
panic!("Unexpected successful payment path: {:?}", path)
},
None => panic!("Unexpected payment_path_failed call: {:?}", actual_path),
None => panic!("Unexpected notify_payment_path_failed call: {:?}", actual_path),
}
}
}
@ -1988,7 +1982,7 @@ mod tests {
Some(TestResult::PaymentSuccess { path }) => {
panic!("Unexpected successful payment path: {:?}", path)
},
None => panic!("Unexpected payment_path_successful call: {:?}", actual_path),
None => panic!("Unexpected notify_payment_path_successful call: {:?}", actual_path),
}
}
}
@ -2132,12 +2126,20 @@ mod tests {
struct ManualRouter(RefCell<VecDeque<Result<Route, LightningError>>>);
impl Router for ManualRouter {
fn find_route<S: Score>(
fn find_route(
&self, _payer: &PublicKey, _params: &RouteParameters, _payment_hash: &PaymentHash,
_first_hops: Option<&[&ChannelDetails]>, _scorer: &S
_first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError> {
self.0.borrow_mut().pop_front().unwrap()
}
fn notify_payment_path_failed(&self, _path: Vec<&RouteHop>, _short_channel_id: u64) {}
fn notify_payment_path_successful(&self, _path: Vec<&RouteHop>) {}
fn notify_payment_probe_successful(&self, _path: Vec<&RouteHop>) {}
fn notify_payment_probe_failed(&self, _path: Vec<&RouteHop>, _short_channel_id: u64) {}
}
impl ManualRouter {
fn expect_find_route(&self, result: Result<Route, LightningError>) {
@ -2193,8 +2195,7 @@ mod tests {
router.expect_find_route(Ok(route.clone()));
let event_handler = |_: &_| { panic!(); };
let scorer = RefCell::new(TestScorer::new());
let invoice_payer = InvoicePayer::new(nodes[0].node, router, &scorer, nodes[0].logger, event_handler, Retry::Attempts(1));
let invoice_payer = InvoicePayer::new(nodes[0].node, router, nodes[0].logger, event_handler, Retry::Attempts(1));
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager_and_duration_since_epoch(
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string(),
@ -2239,8 +2240,7 @@ mod tests {
router.expect_find_route(Ok(route.clone()));
let event_handler = |_: &_| { panic!(); };
let scorer = RefCell::new(TestScorer::new());
let invoice_payer = InvoicePayer::new(nodes[0].node, router, &scorer, nodes[0].logger, event_handler, Retry::Attempts(1));
let invoice_payer = InvoicePayer::new(nodes[0].node, router, nodes[0].logger, event_handler, Retry::Attempts(1));
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager_and_duration_since_epoch(
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string(),
@ -2321,8 +2321,7 @@ mod tests {
let event_checker = expected_events.borrow_mut().pop_front().unwrap();
event_checker(event);
};
let scorer = RefCell::new(TestScorer::new());
let invoice_payer = InvoicePayer::new(nodes[0].node, router, &scorer, nodes[0].logger, event_handler, Retry::Attempts(1));
let invoice_payer = InvoicePayer::new(nodes[0].node, router, nodes[0].logger, event_handler, Retry::Attempts(1));
assert!(invoice_payer.pay_invoice(&create_invoice_from_channelmanager_and_duration_since_epoch(
&nodes[1].node, nodes[1].keys_manager, Currency::Bitcoin, Some(100_010_000), "Invoice".to_string(),

View file

@ -1,7 +1,7 @@
//! Convenient utilities to create an invoice.
use {CreationError, Currency, Invoice, InvoiceBuilder, SignOrCreationError};
use payment::{Payer, Router};
use payment::{InFlightHtlcs, Payer, Router};
use crate::{prelude::*, Description, InvoiceDescription, Sha256};
use bech32::ToBase32;
@ -15,9 +15,9 @@ use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, P
use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA};
use lightning::ln::inbound_payment::{create, create_from_hash, ExpandedKey};
use lightning::ln::msgs::LightningError;
use lightning::routing::gossip::{NetworkGraph, RoutingFees};
use lightning::routing::router::{Route, RouteHint, RouteHintHop, RouteParameters, find_route};
use lightning::routing::scoring::Score;
use lightning::routing::gossip::{NetworkGraph, NodeId, RoutingFees};
use lightning::routing::router::{Route, RouteHint, RouteHintHop, RouteParameters, find_route, RouteHop};
use lightning::routing::scoring::{ChannelUsage, LockableScore, Score};
use lightning::util::logger::Logger;
use secp256k1::PublicKey;
use core::ops::Deref;
@ -440,33 +440,63 @@ fn filter_channels(channels: Vec<ChannelDetails>, min_inbound_capacity_msat: Opt
}
/// A [`Router`] implemented using [`find_route`].
pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>>, L: Deref> where L::Target: Logger {
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> DefaultRouter<G, L> where L::Target: Logger {
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 using the given [`NetworkGraph`], a [`Logger`], and a randomness source
/// `random_seed_bytes`.
pub fn new(network_graph: G, logger: L, random_seed_bytes: [u8; 32]) -> Self {
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 }
Self { network_graph, logger, random_seed_bytes, scorer }
}
}
impl<G: Deref<Target = NetworkGraph<L>>, L: Deref> Router for DefaultRouter<G, L>
where L::Target: Logger {
fn find_route<S: Score>(
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, _payment_hash: &PaymentHash,
first_hops: Option<&[&ChannelDetails]>, scorer: &S
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::hash(&*locked_random_seed_bytes).into_inner();
*locked_random_seed_bytes
};
find_route(payer, params, &self.network_graph, first_hops, &*self.logger, scorer, &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: Vec<&RouteHop>, short_channel_id: u64) {
self.scorer.lock().payment_path_failed(&path, short_channel_id);
}
fn notify_payment_path_successful(&self, path: Vec<&RouteHop>) {
self.scorer.lock().payment_path_successful(&path);
}
fn notify_payment_probe_successful(&self, path: Vec<&RouteHop>) {
self.scorer.lock().probe_successful(&path);
}
fn notify_payment_probe_failed(&self, path: Vec<&RouteHop>, short_channel_id: u64) {
self.scorer.lock().probe_failed(&path, short_channel_id);
}
}
@ -510,6 +540,54 @@ where
}
}
/// Used to store information about all the HTLCs that are inflight across all payment attempts.
pub(crate) 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> {
pub(crate) fn new(scorer: &'a mut S, inflight_htlcs: InFlightHtlcs) -> Self {
ScorerAccountingForInFlightHtlcs {
scorer,
inflight_htlcs
}
}
}
#[cfg(c_bindings)]
impl<'a, S:Score> lightning::util::ser::Writeable for ScorerAccountingForInFlightHtlcs<'a, S> {
fn write<W: lightning::util::ser::Writer>(&self, writer: &mut W) -> Result<(), std::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_liqudity) = self.inflight_htlcs.used_liquidity_msat(
source, target, short_channel_id
) {
let usage = ChannelUsage {
inflight_htlc_msat: usage.inflight_htlc_msat + used_liqudity,
..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) { unreachable!() }
fn payment_path_successful(&mut self, _path: &[&RouteHop]) { unreachable!() }
fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) { unreachable!() }
fn probe_successful(&mut self, _path: &[&RouteHop]) { unreachable!() }
}
#[cfg(test)]
mod test {
use core::time::Duration;