mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 07:17:40 +01:00
Merge pull request #1166 from TheBlueMatt/2021-11-chan-size-scoring
This commit is contained in:
commit
77948dbcd7
9 changed files with 211 additions and 138 deletions
|
@ -39,7 +39,7 @@ use lightning::ln::msgs::DecodeError;
|
|||
use lightning::ln::script::ShutdownScript;
|
||||
use lightning::routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
|
||||
use lightning::routing::router::{find_route, Payee, RouteParameters};
|
||||
use lightning::routing::scorer::Scorer;
|
||||
use lightning::routing::scoring::Scorer;
|
||||
use lightning::util::config::UserConfig;
|
||||
use lightning::util::errors::APIError;
|
||||
use lightning::util::events::Event;
|
||||
|
|
|
@ -17,7 +17,7 @@ use lightning::ln::channelmanager::{ChannelDetails, ChannelCounterparty};
|
|||
use lightning::ln::features::InitFeatures;
|
||||
use lightning::ln::msgs;
|
||||
use lightning::routing::router::{find_route, Payee, RouteHint, RouteHintHop, RouteParameters};
|
||||
use lightning::routing::scorer::Scorer;
|
||||
use lightning::routing::scoring::Scorer;
|
||||
use lightning::util::logger::Logger;
|
||||
use lightning::util::ser::Readable;
|
||||
use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
//! # use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
|
||||
//! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
|
||||
//! # use lightning::ln::msgs::LightningError;
|
||||
//! # use lightning::routing;
|
||||
//! # use lightning::routing::scoring::Score;
|
||||
//! # use lightning::routing::network_graph::NodeId;
|
||||
//! # use lightning::routing::router::{Route, RouteHop, RouteParameters};
|
||||
//! # use lightning::util::events::{Event, EventHandler, EventsProvider};
|
||||
|
@ -63,7 +63,7 @@
|
|||
//! # }
|
||||
//! #
|
||||
//! # struct FakeRouter {};
|
||||
//! # impl<S: routing::Score> Router<S> for FakeRouter {
|
||||
//! # impl<S: Score> Router<S> for FakeRouter {
|
||||
//! # fn find_route(
|
||||
//! # &self, payer: &PublicKey, params: &RouteParameters, payment_hash: &PaymentHash,
|
||||
//! # first_hops: Option<&[&ChannelDetails]>, scorer: &S
|
||||
|
@ -71,9 +71,9 @@
|
|||
//! # }
|
||||
//! #
|
||||
//! # struct FakeScorer {};
|
||||
//! # impl routing::Score for FakeScorer {
|
||||
//! # impl Score for FakeScorer {
|
||||
//! # fn channel_penalty_msat(
|
||||
//! # &self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId
|
||||
//! # &self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId
|
||||
//! # ) -> u64 { 0 }
|
||||
//! # fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
|
||||
//! # }
|
||||
|
@ -122,8 +122,7 @@ use bitcoin_hashes::sha256::Hash as Sha256;
|
|||
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
|
||||
use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
|
||||
use lightning::ln::msgs::LightningError;
|
||||
use lightning::routing;
|
||||
use lightning::routing::{LockableScore, Score};
|
||||
use lightning::routing::scoring::{LockableScore, Score};
|
||||
use lightning::routing::router::{Payee, Route, RouteParameters};
|
||||
use lightning::util::events::{Event, EventHandler};
|
||||
use lightning::util::logger::Logger;
|
||||
|
@ -139,8 +138,8 @@ use std::time::{Duration, SystemTime};
|
|||
pub struct InvoicePayer<P: Deref, R, S: Deref, L: Deref, E>
|
||||
where
|
||||
P::Target: Payer,
|
||||
R: for <'a> Router<<<S as Deref>::Target as routing::LockableScore<'a>>::Locked>,
|
||||
S::Target: for <'a> routing::LockableScore<'a>,
|
||||
R: for <'a> Router<<<S as Deref>::Target as LockableScore<'a>>::Locked>,
|
||||
S::Target: for <'a> LockableScore<'a>,
|
||||
L::Target: Logger,
|
||||
E: EventHandler,
|
||||
{
|
||||
|
@ -177,7 +176,7 @@ pub trait Payer {
|
|||
}
|
||||
|
||||
/// A trait defining behavior for routing an [`Invoice`] payment.
|
||||
pub trait Router<S: routing::Score> {
|
||||
pub trait Router<S: Score> {
|
||||
/// Finds a [`Route`] between `payer` and `payee` for a payment with the given values.
|
||||
fn find_route(
|
||||
&self, payer: &PublicKey, params: &RouteParameters, payment_hash: &PaymentHash,
|
||||
|
@ -207,8 +206,8 @@ pub enum PaymentError {
|
|||
impl<P: Deref, R, S: Deref, L: Deref, E> InvoicePayer<P, R, S, L, E>
|
||||
where
|
||||
P::Target: Payer,
|
||||
R: for <'a> Router<<<S as Deref>::Target as routing::LockableScore<'a>>::Locked>,
|
||||
S::Target: for <'a> routing::LockableScore<'a>,
|
||||
R: for <'a> Router<<<S as Deref>::Target as LockableScore<'a>>::Locked>,
|
||||
S::Target: for <'a> LockableScore<'a>,
|
||||
L::Target: Logger,
|
||||
E: EventHandler,
|
||||
{
|
||||
|
@ -441,8 +440,8 @@ fn has_expired(params: &RouteParameters) -> bool {
|
|||
impl<P: Deref, R, S: Deref, L: Deref, E> EventHandler for InvoicePayer<P, R, S, L, E>
|
||||
where
|
||||
P::Target: Payer,
|
||||
R: for <'a> Router<<<S as Deref>::Target as routing::LockableScore<'a>>::Locked>,
|
||||
S::Target: for <'a> routing::LockableScore<'a>,
|
||||
R: for <'a> Router<<<S as Deref>::Target as LockableScore<'a>>::Locked>,
|
||||
S::Target: for <'a> LockableScore<'a>,
|
||||
L::Target: Logger,
|
||||
E: EventHandler,
|
||||
{
|
||||
|
@ -1186,7 +1185,7 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S: routing::Score> Router<S> for TestRouter {
|
||||
impl<S: Score> Router<S> for TestRouter {
|
||||
fn find_route(
|
||||
&self, _payer: &PublicKey, params: &RouteParameters, _payment_hash: &PaymentHash,
|
||||
_first_hops: Option<&[&ChannelDetails]>, _scorer: &S
|
||||
|
@ -1199,7 +1198,7 @@ mod tests {
|
|||
|
||||
struct FailingRouter;
|
||||
|
||||
impl<S: routing::Score> Router<S> for FailingRouter {
|
||||
impl<S: Score> Router<S> for FailingRouter {
|
||||
fn find_route(
|
||||
&self, _payer: &PublicKey, _params: &RouteParameters, _payment_hash: &PaymentHash,
|
||||
_first_hops: Option<&[&ChannelDetails]>, _scorer: &S
|
||||
|
@ -1225,9 +1224,9 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
impl routing::Score for TestScorer {
|
||||
impl Score for TestScorer {
|
||||
fn channel_penalty_msat(
|
||||
&self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId
|
||||
&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId
|
||||
) -> u64 { 0 }
|
||||
|
||||
fn payment_path_failed(&mut self, _path: &[&RouteHop], short_channel_id: u64) {
|
||||
|
@ -1364,7 +1363,7 @@ mod tests {
|
|||
// *** Full Featured Functional Tests with a Real ChannelManager ***
|
||||
struct ManualRouter(RefCell<VecDeque<Result<Route, LightningError>>>);
|
||||
|
||||
impl<S: routing::Score> Router<S> for ManualRouter {
|
||||
impl<S: Score> Router<S> for ManualRouter {
|
||||
fn find_route(
|
||||
&self, _payer: &PublicKey, _params: &RouteParameters, _payment_hash: &PaymentHash,
|
||||
_first_hops: Option<&[&ChannelDetails]>, _scorer: &S
|
||||
|
|
|
@ -11,7 +11,7 @@ use lightning::chain::keysinterface::{Sign, KeysInterface};
|
|||
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
|
||||
use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY};
|
||||
use lightning::ln::msgs::LightningError;
|
||||
use lightning::routing;
|
||||
use lightning::routing::scoring::Score;
|
||||
use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
|
||||
use lightning::routing::router::{Route, RouteHint, RouteHintHop, RouteParameters, find_route};
|
||||
use lightning::util::logger::Logger;
|
||||
|
@ -109,7 +109,7 @@ impl<G, L: Deref> DefaultRouter<G, L> where G: Deref<Target = NetworkGraph>, L::
|
|||
}
|
||||
}
|
||||
|
||||
impl<G, L: Deref, S: routing::Score> Router<S> for DefaultRouter<G, L>
|
||||
impl<G, L: Deref, S: Score> Router<S> for DefaultRouter<G, L>
|
||||
where G: Deref<Target = NetworkGraph>, L::Target: Logger {
|
||||
fn find_route(
|
||||
&self, payer: &PublicKey, params: &RouteParameters, _payment_hash: &PaymentHash,
|
||||
|
|
|
@ -6552,7 +6552,7 @@ pub mod bench {
|
|||
use ln::msgs::{ChannelMessageHandler, Init};
|
||||
use routing::network_graph::NetworkGraph;
|
||||
use routing::router::{Payee, get_route};
|
||||
use routing::scorer::Scorer;
|
||||
use routing::scoring::Scorer;
|
||||
use util::test_utils;
|
||||
use util::config::UserConfig;
|
||||
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose};
|
||||
|
|
|
@ -11,65 +11,4 @@
|
|||
|
||||
pub mod network_graph;
|
||||
pub mod router;
|
||||
pub mod scorer;
|
||||
|
||||
use routing::network_graph::NodeId;
|
||||
use routing::router::RouteHop;
|
||||
|
||||
use core::cell::{RefCell, RefMut};
|
||||
use core::ops::DerefMut;
|
||||
use sync::{Mutex, MutexGuard};
|
||||
|
||||
/// 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 {
|
||||
/// Returns the fee in msats willing to be paid to avoid routing through the given channel
|
||||
/// in the direction from `source` to `target`.
|
||||
fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId) -> u64;
|
||||
|
||||
/// Handles updating channel penalties after failing to route through a channel.
|
||||
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64);
|
||||
}
|
||||
|
||||
/// 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`]
|
||||
/// 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.
|
||||
///
|
||||
/// [`find_route`]: crate::routing::router::find_route
|
||||
pub trait LockableScore<'a> {
|
||||
/// The locked [`Score`] type.
|
||||
type Locked: 'a + Score;
|
||||
|
||||
/// Returns the locked scorer.
|
||||
fn lock(&'a self) -> Self::Locked;
|
||||
}
|
||||
|
||||
impl<'a, T: 'a + Score> LockableScore<'a> for Mutex<T> {
|
||||
type Locked = MutexGuard<'a, T>;
|
||||
|
||||
fn lock(&'a self) -> MutexGuard<'a, T> {
|
||||
Mutex::lock(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a + Score> LockableScore<'a> for RefCell<T> {
|
||||
type Locked = RefMut<'a, T>;
|
||||
|
||||
fn lock(&'a self) -> RefMut<'a, T> {
|
||||
self.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Score, T: DerefMut<Target=S>> Score for T {
|
||||
fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId) -> u64 {
|
||||
self.deref().channel_penalty_msat(short_channel_id, source, target)
|
||||
}
|
||||
|
||||
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
|
||||
self.deref_mut().payment_path_failed(path, short_channel_id)
|
||||
}
|
||||
}
|
||||
pub mod scoring;
|
||||
|
|
|
@ -17,7 +17,7 @@ use bitcoin::secp256k1::key::PublicKey;
|
|||
use ln::channelmanager::ChannelDetails;
|
||||
use ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures};
|
||||
use ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
|
||||
use routing;
|
||||
use routing::scoring::Score;
|
||||
use routing::network_graph::{NetworkGraph, NodeId, RoutingFees};
|
||||
use util::ser::{Writeable, Readable};
|
||||
use util::logger::{Level, Logger};
|
||||
|
@ -529,7 +529,7 @@ fn compute_fees(amount_msat: u64, channel_fees: RoutingFees) -> Option<u64> {
|
|||
///
|
||||
/// [`ChannelManager::list_usable_channels`]: crate::ln::channelmanager::ChannelManager::list_usable_channels
|
||||
/// [`Event::PaymentPathFailed`]: crate::util::events::Event::PaymentPathFailed
|
||||
pub fn find_route<L: Deref, S: routing::Score>(
|
||||
pub fn find_route<L: Deref, S: Score>(
|
||||
our_node_pubkey: &PublicKey, params: &RouteParameters, network: &NetworkGraph,
|
||||
first_hops: Option<&[&ChannelDetails]>, logger: L, scorer: &S
|
||||
) -> Result<Route, LightningError>
|
||||
|
@ -540,7 +540,7 @@ where L::Target: Logger {
|
|||
)
|
||||
}
|
||||
|
||||
pub(crate) fn get_route<L: Deref, S: routing::Score>(
|
||||
pub(crate) fn get_route<L: Deref, S: Score>(
|
||||
our_node_pubkey: &PublicKey, payee: &Payee, network: &NetworkGraph,
|
||||
first_hops: Option<&[&ChannelDetails]>, final_value_msat: u64, final_cltv_expiry_delta: u32,
|
||||
logger: L, scorer: &S
|
||||
|
@ -892,9 +892,9 @@ where L::Target: Logger {
|
|||
}
|
||||
}
|
||||
|
||||
let path_penalty_msat = $next_hops_path_penalty_msat
|
||||
.checked_add(scorer.channel_penalty_msat($chan_id.clone(), &$src_node_id, &$dest_node_id))
|
||||
.unwrap_or_else(|| u64::max_value());
|
||||
let path_penalty_msat = $next_hops_path_penalty_msat.checked_add(
|
||||
scorer.channel_penalty_msat($chan_id.clone(), amount_to_transfer_over_msat, Some(*available_liquidity_msat),
|
||||
&$src_node_id, &$dest_node_id)).unwrap_or_else(|| u64::max_value());
|
||||
let new_graph_node = RouteGraphNode {
|
||||
node_id: $src_node_id,
|
||||
lowest_fee_to_peer_through_node: total_fee_msat,
|
||||
|
@ -1121,7 +1121,7 @@ where L::Target: Logger {
|
|||
let src_node_id = NodeId::from_pubkey(&hop.src_node_id);
|
||||
let dest_node_id = NodeId::from_pubkey(&prev_hop_id);
|
||||
aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
|
||||
.checked_add(scorer.channel_penalty_msat(hop.short_channel_id, &src_node_id, &dest_node_id))
|
||||
.checked_add(scorer.channel_penalty_msat(hop.short_channel_id, final_value_msat, None, &src_node_id, &dest_node_id))
|
||||
.unwrap_or_else(|| u64::max_value());
|
||||
|
||||
// We assume that the recipient only included route hints for routes which had
|
||||
|
@ -1472,7 +1472,7 @@ where L::Target: Logger {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use routing;
|
||||
use routing::scoring::Score;
|
||||
use routing::network_graph::{NetworkGraph, NetGraphMsgHandler, NodeId};
|
||||
use routing::router::{get_route, Payee, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees};
|
||||
use chain::transaction::OutPoint;
|
||||
|
@ -4549,8 +4549,8 @@ mod tests {
|
|||
short_channel_id: u64,
|
||||
}
|
||||
|
||||
impl routing::Score for BadChannelScorer {
|
||||
fn channel_penalty_msat(&self, short_channel_id: u64, _source: &NodeId, _target: &NodeId) -> u64 {
|
||||
impl Score for BadChannelScorer {
|
||||
fn channel_penalty_msat(&self, short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, _target: &NodeId) -> u64 {
|
||||
if short_channel_id == self.short_channel_id { u64::max_value() } else { 0 }
|
||||
}
|
||||
|
||||
|
@ -4561,8 +4561,8 @@ mod tests {
|
|||
node_id: NodeId,
|
||||
}
|
||||
|
||||
impl routing::Score for BadNodeScorer {
|
||||
fn channel_penalty_msat(&self, _short_channel_id: u64, _source: &NodeId, target: &NodeId) -> u64 {
|
||||
impl Score for BadNodeScorer {
|
||||
fn channel_penalty_msat(&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: Option<u64>, _source: &NodeId, target: &NodeId) -> u64 {
|
||||
if *target == self.node_id { u64::max_value() } else { 0 }
|
||||
}
|
||||
|
||||
|
@ -4787,7 +4787,7 @@ pub(crate) mod test_utils {
|
|||
#[cfg(all(test, feature = "unstable", not(feature = "no-std")))]
|
||||
mod benches {
|
||||
use super::*;
|
||||
use routing::scorer::Scorer;
|
||||
use routing::scoring::Scorer;
|
||||
use util::logger::{Logger, Record};
|
||||
|
||||
use test::Bencher;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
//! Utilities for scoring payment channels.
|
||||
//!
|
||||
//! [`Scorer`] may be given to [`find_route`] to score payment channels during path finding when a
|
||||
//! custom [`routing::Score`] implementation is not needed.
|
||||
//! custom [`Score`] implementation is not needed.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
|
@ -19,7 +19,7 @@
|
|||
//! #
|
||||
//! # use lightning::routing::network_graph::NetworkGraph;
|
||||
//! # use lightning::routing::router::{RouteParameters, find_route};
|
||||
//! # use lightning::routing::scorer::{Scorer, ScoringParameters};
|
||||
//! # use lightning::routing::scoring::{Scorer, ScoringParameters};
|
||||
//! # use lightning::util::logger::{Logger, Record};
|
||||
//! # use secp256k1::key::PublicKey;
|
||||
//! #
|
||||
|
@ -52,26 +52,89 @@
|
|||
//!
|
||||
//! [`find_route`]: crate::routing::router::find_route
|
||||
|
||||
use routing;
|
||||
|
||||
use ln::msgs::DecodeError;
|
||||
use routing::network_graph::NodeId;
|
||||
use routing::router::RouteHop;
|
||||
use util::ser::{Readable, Writeable, Writer};
|
||||
|
||||
use prelude::*;
|
||||
use core::ops::Sub;
|
||||
use core::cell::{RefCell, RefMut};
|
||||
use core::ops::{DerefMut, Sub};
|
||||
use core::time::Duration;
|
||||
use io::{self, Read};
|
||||
use io::{self, Read}; use sync::{Mutex, MutexGuard};
|
||||
|
||||
/// [`routing::Score`] implementation that provides reasonable default behavior.
|
||||
/// 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 {
|
||||
/// Returns the fee in msats willing to be paid to avoid routing `send_amt_msat` through the
|
||||
/// given channel in the direction from `source` to `target`.
|
||||
///
|
||||
/// The channel's capacity (less any other MPP parts which are also being considered for use in
|
||||
/// the same payment) is given by `channel_capacity_msat`. It may be guessed from various
|
||||
/// sources or assumed from no data at all.
|
||||
///
|
||||
/// For hints provided in the invoice, we assume the channel has sufficient capacity to accept
|
||||
/// the invoice's full amount, and provide a `channel_capacity_msat` of `None`. In all other
|
||||
/// cases it is set to `Some`, even if we're guessing at the channel value.
|
||||
///
|
||||
/// Your code should be overflow-safe through a `channel_capacity_msat` of 21 million BTC.
|
||||
fn channel_penalty_msat(&self, short_channel_id: u64, send_amt_msat: u64, channel_capacity_msat: Option<u64>, source: &NodeId, target: &NodeId) -> u64;
|
||||
|
||||
/// Handles updating channel penalties after failing to route through a channel.
|
||||
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64);
|
||||
}
|
||||
|
||||
/// 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`]
|
||||
/// 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.
|
||||
///
|
||||
/// [`find_route`]: crate::routing::router::find_route
|
||||
pub trait LockableScore<'a> {
|
||||
/// The locked [`Score`] type.
|
||||
type Locked: 'a + Score;
|
||||
|
||||
/// Returns the locked scorer.
|
||||
fn lock(&'a self) -> Self::Locked;
|
||||
}
|
||||
|
||||
impl<'a, T: 'a + Score> LockableScore<'a> for Mutex<T> {
|
||||
type Locked = MutexGuard<'a, T>;
|
||||
|
||||
fn lock(&'a self) -> MutexGuard<'a, T> {
|
||||
Mutex::lock(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a + Score> LockableScore<'a> for RefCell<T> {
|
||||
type Locked = RefMut<'a, T>;
|
||||
|
||||
fn lock(&'a self) -> RefMut<'a, T> {
|
||||
self.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Score, T: DerefMut<Target=S>> Score for T {
|
||||
fn channel_penalty_msat(&self, short_channel_id: u64, send_amt_msat: u64, channel_capacity_msat: Option<u64>, source: &NodeId, target: &NodeId) -> u64 {
|
||||
self.deref().channel_penalty_msat(short_channel_id, send_amt_msat, channel_capacity_msat, source, target)
|
||||
}
|
||||
|
||||
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
|
||||
self.deref_mut().payment_path_failed(path, short_channel_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Score`] implementation that provides reasonable default behavior.
|
||||
///
|
||||
/// Used to apply a fixed penalty to each channel, thus avoiding long paths when shorter paths with
|
||||
/// slightly higher fees are available. Will further penalize channels that fail to relay payments.
|
||||
///
|
||||
/// See [module-level documentation] for usage.
|
||||
///
|
||||
/// [module-level documentation]: crate::routing::scorer
|
||||
/// [module-level documentation]: crate::routing::scoring
|
||||
pub type Scorer = ScorerUsingTime::<DefaultTime>;
|
||||
|
||||
/// Time used by [`Scorer`].
|
||||
|
@ -82,7 +145,7 @@ pub type DefaultTime = std::time::Instant;
|
|||
#[cfg(feature = "no-std")]
|
||||
pub type DefaultTime = Eternity;
|
||||
|
||||
/// [`routing::Score`] implementation parameterized by [`Time`].
|
||||
/// [`Score`] implementation parameterized by [`Time`].
|
||||
///
|
||||
/// See [`Scorer`] for details.
|
||||
///
|
||||
|
@ -98,6 +161,8 @@ pub struct ScorerUsingTime<T: Time> {
|
|||
/// Parameters for configuring [`Scorer`].
|
||||
pub struct ScoringParameters {
|
||||
/// A fixed penalty in msats to apply to each channel.
|
||||
///
|
||||
/// Default value: 500 msat
|
||||
pub base_penalty_msat: u64,
|
||||
|
||||
/// A penalty in msats to apply to a channel upon failing to relay a payment.
|
||||
|
@ -105,9 +170,28 @@ pub struct ScoringParameters {
|
|||
/// This accumulates for each failure but may be reduced over time based on
|
||||
/// [`failure_penalty_half_life`].
|
||||
///
|
||||
/// Default value: 1,024,000 msat
|
||||
///
|
||||
/// [`failure_penalty_half_life`]: Self::failure_penalty_half_life
|
||||
pub failure_penalty_msat: u64,
|
||||
|
||||
/// When the amount being sent over a channel is this many 1024ths of the total channel
|
||||
/// capacity, we begin applying [`overuse_penalty_msat_per_1024th`].
|
||||
///
|
||||
/// Default value: 128 1024ths (i.e. begin penalizing when an HTLC uses 1/8th of a channel)
|
||||
///
|
||||
/// [`overuse_penalty_msat_per_1024th`]: Self::overuse_penalty_msat_per_1024th
|
||||
pub overuse_penalty_start_1024th: u16,
|
||||
|
||||
/// A penalty applied, per whole 1024ths of the channel capacity which the amount being sent
|
||||
/// over the channel exceeds [`overuse_penalty_start_1024th`] by.
|
||||
///
|
||||
/// Default value: 20 msat (i.e. 2560 msat penalty to use 1/4th of a channel, 7680 msat penalty
|
||||
/// to use half a channel, and 12,560 msat penalty to use 3/4ths of a channel)
|
||||
///
|
||||
/// [`overuse_penalty_start_1024th`]: Self::overuse_penalty_start_1024th
|
||||
pub overuse_penalty_msat_per_1024th: u64,
|
||||
|
||||
/// The time required to elapse before any accumulated [`failure_penalty_msat`] penalties are
|
||||
/// cut in half.
|
||||
///
|
||||
|
@ -122,7 +206,9 @@ pub struct ScoringParameters {
|
|||
|
||||
impl_writeable_tlv_based!(ScoringParameters, {
|
||||
(0, base_penalty_msat, required),
|
||||
(1, overuse_penalty_start_1024th, (default_value, 128)),
|
||||
(2, failure_penalty_msat, required),
|
||||
(3, overuse_penalty_msat_per_1024th, (default_value, 20)),
|
||||
(4, failure_penalty_half_life, required),
|
||||
});
|
||||
|
||||
|
@ -167,6 +253,8 @@ impl<T: Time> ScorerUsingTime<T> {
|
|||
base_penalty_msat: penalty_msat,
|
||||
failure_penalty_msat: 0,
|
||||
failure_penalty_half_life: Duration::from_secs(0),
|
||||
overuse_penalty_start_1024th: 1024,
|
||||
overuse_penalty_msat_per_1024th: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -205,19 +293,34 @@ impl Default for ScoringParameters {
|
|||
base_penalty_msat: 500,
|
||||
failure_penalty_msat: 1024 * 1000,
|
||||
failure_penalty_half_life: Duration::from_secs(3600),
|
||||
overuse_penalty_start_1024th: 1024 / 8,
|
||||
overuse_penalty_msat_per_1024th: 20,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Time> routing::Score for ScorerUsingTime<T> {
|
||||
impl<T: Time> Score for ScorerUsingTime<T> {
|
||||
fn channel_penalty_msat(
|
||||
&self, short_channel_id: u64, _source: &NodeId, _target: &NodeId
|
||||
&self, short_channel_id: u64, send_amt_msat: u64, chan_capacity_opt: Option<u64>, _source: &NodeId, _target: &NodeId
|
||||
) -> u64 {
|
||||
let failure_penalty_msat = self.channel_failures
|
||||
.get(&short_channel_id)
|
||||
.map_or(0, |value| value.decayed_penalty_msat(self.params.failure_penalty_half_life));
|
||||
|
||||
self.params.base_penalty_msat + failure_penalty_msat
|
||||
let mut penalty_msat = self.params.base_penalty_msat + failure_penalty_msat;
|
||||
|
||||
if let Some(chan_capacity_msat) = chan_capacity_opt {
|
||||
let send_1024ths = send_amt_msat.checked_mul(1024).unwrap_or(u64::max_value()) / chan_capacity_msat;
|
||||
|
||||
if send_1024ths > self.params.overuse_penalty_start_1024th as u64 {
|
||||
penalty_msat = penalty_msat.checked_add(
|
||||
(send_1024ths - self.params.overuse_penalty_start_1024th as u64)
|
||||
.checked_mul(self.params.overuse_penalty_msat_per_1024th).unwrap_or(u64::max_value()))
|
||||
.unwrap_or(u64::max_value());
|
||||
}
|
||||
}
|
||||
|
||||
penalty_msat
|
||||
}
|
||||
|
||||
fn payment_path_failed(&mut self, _path: &[&RouteHop], short_channel_id: u64) {
|
||||
|
@ -326,7 +429,7 @@ impl<T: Time> Readable for ChannelFailure<T> {
|
|||
mod tests {
|
||||
use super::{Eternity, ScoringParameters, ScorerUsingTime, Time};
|
||||
|
||||
use routing::Score;
|
||||
use routing::scoring::Score;
|
||||
use routing::network_graph::NodeId;
|
||||
use util::ser::{Readable, Writeable};
|
||||
|
||||
|
@ -414,13 +517,15 @@ mod tests {
|
|||
base_penalty_msat: 1_000,
|
||||
failure_penalty_msat: 512,
|
||||
failure_penalty_half_life: Duration::from_secs(1),
|
||||
overuse_penalty_start_1024th: 1024,
|
||||
overuse_penalty_msat_per_1024th: 0,
|
||||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(1));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -429,19 +534,21 @@ mod tests {
|
|||
base_penalty_msat: 1_000,
|
||||
failure_penalty_msat: 64,
|
||||
failure_penalty_half_life: Duration::from_secs(10),
|
||||
overuse_penalty_start_1024th: 1024,
|
||||
overuse_penalty_msat_per_1024th: 0,
|
||||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_064);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_064);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_128);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_128);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_192);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_192);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -450,28 +557,30 @@ mod tests {
|
|||
base_penalty_msat: 1_000,
|
||||
failure_penalty_msat: 512,
|
||||
failure_penalty_half_life: Duration::from_secs(10),
|
||||
overuse_penalty_start_1024th: 1024,
|
||||
overuse_penalty_msat_per_1024th: 0,
|
||||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_512);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(9));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_512);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(1));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_256);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_256);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10 * 8));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_001);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_001);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -480,22 +589,24 @@ mod tests {
|
|||
base_penalty_msat: 1_000,
|
||||
failure_penalty_msat: 512,
|
||||
failure_penalty_half_life: Duration::from_secs(10),
|
||||
overuse_penalty_start_1024th: 1024,
|
||||
overuse_penalty_msat_per_1024th: 0,
|
||||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_000);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_512);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_256);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_256);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_768);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_768);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_384);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_384);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -504,25 +615,27 @@ mod tests {
|
|||
base_penalty_msat: 1_000,
|
||||
failure_penalty_msat: 512,
|
||||
failure_penalty_half_life: Duration::from_secs(10),
|
||||
overuse_penalty_start_1024th: 1024,
|
||||
overuse_penalty_msat_per_1024th: 0,
|
||||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_512);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_256);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_256);
|
||||
|
||||
scorer.payment_path_failed(&[], 43);
|
||||
assert_eq!(scorer.channel_penalty_msat(43, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(43, 1, Some(1), &source, &target), 1_512);
|
||||
|
||||
let mut serialized_scorer = Vec::new();
|
||||
scorer.write(&mut serialized_scorer).unwrap();
|
||||
|
||||
let deserialized_scorer = <Scorer>::read(&mut io::Cursor::new(&serialized_scorer)).unwrap();
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target), 1_256);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(43, &source, &target), 1_512);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_256);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(43, 1, Some(1), &source, &target), 1_512);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -531,12 +644,14 @@ mod tests {
|
|||
base_penalty_msat: 1_000,
|
||||
failure_penalty_msat: 512,
|
||||
failure_penalty_half_life: Duration::from_secs(10),
|
||||
overuse_penalty_start_1024th: 1024,
|
||||
overuse_penalty_msat_per_1024th: 0,
|
||||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_512);
|
||||
|
||||
let mut serialized_scorer = Vec::new();
|
||||
scorer.write(&mut serialized_scorer).unwrap();
|
||||
|
@ -544,9 +659,29 @@ mod tests {
|
|||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
|
||||
let deserialized_scorer = <Scorer>::read(&mut io::Cursor::new(&serialized_scorer)).unwrap();
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target), 1_256);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_256);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target), 1_128);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 1, Some(1), &source, &target), 1_128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn charges_per_1024th_penalty() {
|
||||
let scorer = Scorer::new(ScoringParameters {
|
||||
base_penalty_msat: 0,
|
||||
failure_penalty_msat: 0,
|
||||
failure_penalty_half_life: Duration::from_secs(0),
|
||||
overuse_penalty_start_1024th: 256,
|
||||
overuse_penalty_msat_per_1024th: 100,
|
||||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1_000, None, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1_000, Some(1_024_000), &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 256_999, Some(1_024_000), &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 257_000, Some(1_024_000), &source, &target), 100);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 258_000, Some(1_024_000), &source, &target), 200);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512_000, Some(1_024_000), &source, &target), 256 * 100);
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ use ln::features::{ChannelFeatures, InitFeatures};
|
|||
use ln::msgs;
|
||||
use ln::msgs::OptionalField;
|
||||
use ln::script::ShutdownScript;
|
||||
use routing::scorer::{Eternity, ScorerUsingTime};
|
||||
use routing::scoring::{Eternity, ScorerUsingTime};
|
||||
use util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
|
||||
use util::events;
|
||||
use util::logger::{Logger, Level, Record};
|
||||
|
|
Loading…
Add table
Reference in a new issue