mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 07:17:40 +01:00
Merge pull request #1456 from jkczyz/2022-04-effective-capacity
Use `EffectiveCapacity` in `Score` trait
This commit is contained in:
commit
0c6974b602
4 changed files with 530 additions and 207 deletions
|
@ -38,9 +38,9 @@
|
|||
//! # use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
|
||||
//! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
|
||||
//! # use lightning::ln::msgs::LightningError;
|
||||
//! # use lightning::routing::scoring::Score;
|
||||
//! # use lightning::routing::network_graph::NodeId;
|
||||
//! # use lightning::routing::router::{Route, RouteHop, RouteParameters};
|
||||
//! # use lightning::routing::scoring::{ChannelUsage, Score};
|
||||
//! # use lightning::util::events::{Event, EventHandler, EventsProvider};
|
||||
//! # use lightning::util::logger::{Logger, Record};
|
||||
//! # use lightning::util::ser::{Writeable, Writer};
|
||||
|
@ -90,7 +90,7 @@
|
|||
//! # }
|
||||
//! # impl Score for FakeScorer {
|
||||
//! # fn channel_penalty_msat(
|
||||
//! # &self, _short_channel_id: u64, _send_amt: u64, _chan_amt: u64, _source: &NodeId, _target: &NodeId
|
||||
//! # &self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId, _usage: ChannelUsage
|
||||
//! # ) -> u64 { 0 }
|
||||
//! # fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
|
||||
//! # fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
|
||||
|
@ -604,6 +604,7 @@ mod tests {
|
|||
use lightning::ln::msgs::{ChannelMessageHandler, ErrorAction, LightningError};
|
||||
use lightning::routing::network_graph::NodeId;
|
||||
use lightning::routing::router::{PaymentParameters, Route, RouteHop};
|
||||
use lightning::routing::scoring::ChannelUsage;
|
||||
use lightning::util::test_utils::TestLogger;
|
||||
use lightning::util::errors::APIError;
|
||||
use lightning::util::events::{Event, EventsProvider, MessageSendEvent, MessageSendEventsProvider};
|
||||
|
@ -1444,7 +1445,7 @@ mod tests {
|
|||
|
||||
impl Score for TestScorer {
|
||||
fn channel_penalty_msat(
|
||||
&self, _short_channel_id: u64, _send_amt: u64, _chan_amt: u64, _source: &NodeId, _target: &NodeId
|
||||
&self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId, _usage: ChannelUsage
|
||||
) -> u64 { 0 }
|
||||
|
||||
fn payment_path_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) {
|
||||
|
|
|
@ -695,7 +695,7 @@ impl ChannelInfo {
|
|||
return None;
|
||||
}
|
||||
};
|
||||
Some((DirectedChannelInfo { channel: self, direction }, source))
|
||||
Some((DirectedChannelInfo::new(self, direction), source))
|
||||
}
|
||||
|
||||
/// Returns a [`DirectedChannelInfo`] for the channel directed from the given `source` to a
|
||||
|
@ -710,7 +710,7 @@ impl ChannelInfo {
|
|||
return None;
|
||||
}
|
||||
};
|
||||
Some((DirectedChannelInfo { channel: self, direction }, target))
|
||||
Some((DirectedChannelInfo::new(self, direction), target))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -739,35 +739,53 @@ impl_writeable_tlv_based!(ChannelInfo, {
|
|||
pub struct DirectedChannelInfo<'a> {
|
||||
channel: &'a ChannelInfo,
|
||||
direction: Option<&'a ChannelUpdateInfo>,
|
||||
htlc_maximum_msat: u64,
|
||||
effective_capacity: EffectiveCapacity,
|
||||
}
|
||||
|
||||
impl<'a> DirectedChannelInfo<'a> {
|
||||
#[inline]
|
||||
fn new(channel: &'a ChannelInfo, direction: Option<&'a ChannelUpdateInfo>) -> Self {
|
||||
let htlc_maximum_msat = direction.and_then(|direction| direction.htlc_maximum_msat);
|
||||
let capacity_msat = channel.capacity_sats.map(|capacity_sats| capacity_sats * 1000);
|
||||
|
||||
let (htlc_maximum_msat, effective_capacity) = match (htlc_maximum_msat, capacity_msat) {
|
||||
(Some(amount_msat), Some(capacity_msat)) => {
|
||||
let htlc_maximum_msat = cmp::min(amount_msat, capacity_msat);
|
||||
(htlc_maximum_msat, EffectiveCapacity::Total { capacity_msat })
|
||||
},
|
||||
(Some(amount_msat), None) => {
|
||||
(amount_msat, EffectiveCapacity::MaximumHTLC { amount_msat })
|
||||
},
|
||||
(None, Some(capacity_msat)) => {
|
||||
(capacity_msat, EffectiveCapacity::Total { capacity_msat })
|
||||
},
|
||||
(None, None) => (EffectiveCapacity::Unknown.as_msat(), EffectiveCapacity::Unknown),
|
||||
};
|
||||
|
||||
Self {
|
||||
channel, direction, htlc_maximum_msat, effective_capacity
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns information for the channel.
|
||||
pub fn channel(&self) -> &'a ChannelInfo { self.channel }
|
||||
|
||||
/// Returns information for the direction.
|
||||
pub fn direction(&self) -> Option<&'a ChannelUpdateInfo> { self.direction }
|
||||
|
||||
/// Returns the maximum HTLC amount allowed over the channel in the direction.
|
||||
pub fn htlc_maximum_msat(&self) -> u64 {
|
||||
self.htlc_maximum_msat
|
||||
}
|
||||
|
||||
/// Returns the [`EffectiveCapacity`] of the channel in the direction.
|
||||
///
|
||||
/// This is either the total capacity from the funding transaction, if known, or the
|
||||
/// `htlc_maximum_msat` for the direction as advertised by the gossip network, if known,
|
||||
/// whichever is smaller.
|
||||
/// otherwise.
|
||||
pub fn effective_capacity(&self) -> EffectiveCapacity {
|
||||
let capacity_msat = self.channel.capacity_sats.map(|capacity_sats| capacity_sats * 1000);
|
||||
self.direction
|
||||
.and_then(|direction| direction.htlc_maximum_msat)
|
||||
.map(|max_htlc_msat| {
|
||||
let capacity_msat = capacity_msat.unwrap_or(u64::max_value());
|
||||
if max_htlc_msat < capacity_msat {
|
||||
EffectiveCapacity::MaximumHTLC { amount_msat: max_htlc_msat }
|
||||
} else {
|
||||
EffectiveCapacity::Total { capacity_msat }
|
||||
}
|
||||
})
|
||||
.or_else(|| capacity_msat.map(|capacity_msat|
|
||||
EffectiveCapacity::Total { capacity_msat }))
|
||||
.unwrap_or(EffectiveCapacity::Unknown)
|
||||
self.effective_capacity
|
||||
}
|
||||
|
||||
/// Returns `Some` if [`ChannelUpdateInfo`] is available in the direction.
|
||||
|
@ -805,6 +823,10 @@ impl<'a> DirectedChannelInfoWithUpdate<'a> {
|
|||
/// Returns the [`EffectiveCapacity`] of the channel in the direction.
|
||||
#[inline]
|
||||
pub(super) fn effective_capacity(&self) -> EffectiveCapacity { self.inner.effective_capacity() }
|
||||
|
||||
/// Returns the maximum HTLC amount allowed over the channel in the direction.
|
||||
#[inline]
|
||||
pub(super) fn htlc_maximum_msat(&self) -> u64 { self.inner.htlc_maximum_msat() }
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for DirectedChannelInfoWithUpdate<'a> {
|
||||
|
@ -817,6 +839,7 @@ impl<'a> fmt::Debug for DirectedChannelInfoWithUpdate<'a> {
|
|||
///
|
||||
/// While this may be smaller than the actual channel capacity, amounts greater than
|
||||
/// [`Self::as_msat`] should not be routed through the channel.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum EffectiveCapacity {
|
||||
/// The available liquidity in the channel known from being a channel counterparty, and thus a
|
||||
/// direct hop.
|
||||
|
|
|
@ -17,7 +17,7 @@ use bitcoin::secp256k1::PublicKey;
|
|||
use ln::channelmanager::ChannelDetails;
|
||||
use ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures};
|
||||
use ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
|
||||
use routing::scoring::Score;
|
||||
use routing::scoring::{ChannelUsage, Score};
|
||||
use routing::network_graph::{DirectedChannelInfoWithUpdate, EffectiveCapacity, NetworkGraph, ReadOnlyNetworkGraph, NodeId, RoutingFees};
|
||||
use util::ser::{Writeable, Readable};
|
||||
use util::logger::{Level, Logger};
|
||||
|
@ -414,6 +414,16 @@ impl<'a> CandidateRouteHop<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn htlc_maximum_msat(&self) -> u64 {
|
||||
match self {
|
||||
CandidateRouteHop::FirstHop { details } => details.next_outbound_htlc_limit_msat,
|
||||
CandidateRouteHop::PublicHop { info, .. } => info.htlc_maximum_msat(),
|
||||
CandidateRouteHop::PrivateHop { hint } => {
|
||||
hint.htlc_maximum_msat.unwrap_or(u64::max_value())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn fees(&self) -> RoutingFees {
|
||||
match self {
|
||||
CandidateRouteHop::FirstHop { .. } => RoutingFees {
|
||||
|
@ -481,7 +491,8 @@ struct PathBuildingHop<'a> {
|
|||
|
||||
impl<'a> core::fmt::Debug for PathBuildingHop<'a> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
|
||||
f.debug_struct("PathBuildingHop")
|
||||
let mut debug_struct = f.debug_struct("PathBuildingHop");
|
||||
debug_struct
|
||||
.field("node_id", &self.node_id)
|
||||
.field("short_channel_id", &self.candidate.short_channel_id())
|
||||
.field("total_fee_msat", &self.total_fee_msat)
|
||||
|
@ -490,8 +501,11 @@ impl<'a> core::fmt::Debug for PathBuildingHop<'a> {
|
|||
.field("total_fee_msat - (next_hops_fee_msat + hop_use_fee_msat)", &(&self.total_fee_msat - (&self.next_hops_fee_msat + &self.hop_use_fee_msat)))
|
||||
.field("path_penalty_msat", &self.path_penalty_msat)
|
||||
.field("path_htlc_minimum_msat", &self.path_htlc_minimum_msat)
|
||||
.field("cltv_expiry_delta", &self.candidate.cltv_expiry_delta())
|
||||
.finish()
|
||||
.field("cltv_expiry_delta", &self.candidate.cltv_expiry_delta());
|
||||
#[cfg(all(not(feature = "_bench_unstable"), any(test, fuzzing)))]
|
||||
let debug_struct = debug_struct
|
||||
.field("value_contribution_msat", &self.value_contribution_msat);
|
||||
debug_struct.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -830,12 +844,12 @@ where L::Target: Logger {
|
|||
let recommended_value_msat = final_value_msat * ROUTE_CAPACITY_PROVISION_FACTOR as u64;
|
||||
let mut path_value_msat = final_value_msat;
|
||||
|
||||
// We don't want multiple paths (as per MPP) share liquidity of the same channels.
|
||||
// This map allows paths to be aware of the channel use by other paths in the same call.
|
||||
// This would help to make a better path finding decisions and not "overbook" channels.
|
||||
// It is unaware of the directions (except for `next_outbound_htlc_limit_msat` in
|
||||
// `first_hops`).
|
||||
let mut bookkept_channels_liquidity_available_msat = HashMap::with_capacity(network_nodes.len());
|
||||
// Keep track of how much liquidity has been used in selected channels. Used to determine
|
||||
// if the channel can be used by additional MPP paths or to inform path finding decisions. It is
|
||||
// aware of direction *only* to ensure that the correct htlc_maximum_msat value is used. Hence,
|
||||
// liquidity used in one direction will not offset any used in the opposite direction.
|
||||
let mut used_channel_liquidities: HashMap<(u64, bool), u64> =
|
||||
HashMap::with_capacity(network_nodes.len());
|
||||
|
||||
// Keeping track of how much value we already collected across other paths. Helps to decide:
|
||||
// - how much a new path should be transferring (upper bound);
|
||||
|
@ -885,9 +899,7 @@ where L::Target: Logger {
|
|||
// - for first and last hops early in get_route
|
||||
if $src_node_id != $dest_node_id {
|
||||
let short_channel_id = $candidate.short_channel_id();
|
||||
let available_liquidity_msat = bookkept_channels_liquidity_available_msat
|
||||
.entry(short_channel_id)
|
||||
.or_insert_with(|| $candidate.effective_capacity().as_msat());
|
||||
let htlc_maximum_msat = $candidate.htlc_maximum_msat();
|
||||
|
||||
// It is tricky to subtract $next_hops_fee_msat from available liquidity here.
|
||||
// It may be misleading because we might later choose to reduce the value transferred
|
||||
|
@ -896,7 +908,14 @@ where L::Target: Logger {
|
|||
// fees caused by one expensive channel, but then this channel could have been used
|
||||
// if the amount being transferred over this path is lower.
|
||||
// We do this for now, but this is a subject for removal.
|
||||
if let Some(available_value_contribution_msat) = available_liquidity_msat.checked_sub($next_hops_fee_msat) {
|
||||
if let Some(mut available_value_contribution_msat) = htlc_maximum_msat.checked_sub($next_hops_fee_msat) {
|
||||
let used_liquidity_msat = used_channel_liquidities
|
||||
.get(&(short_channel_id, $src_node_id < $dest_node_id))
|
||||
.map_or(0, |used_liquidity_msat| {
|
||||
available_value_contribution_msat = available_value_contribution_msat
|
||||
.saturating_sub(*used_liquidity_msat);
|
||||
*used_liquidity_msat
|
||||
});
|
||||
|
||||
// Routing Fragmentation Mitigation heuristic:
|
||||
//
|
||||
|
@ -1047,9 +1066,16 @@ where L::Target: Logger {
|
|||
}
|
||||
}
|
||||
|
||||
let path_penalty_msat = $next_hops_path_penalty_msat.saturating_add(
|
||||
scorer.channel_penalty_msat(short_channel_id, amount_to_transfer_over_msat,
|
||||
*available_liquidity_msat, &$src_node_id, &$dest_node_id));
|
||||
let channel_usage = ChannelUsage {
|
||||
amount_msat: amount_to_transfer_over_msat,
|
||||
inflight_htlc_msat: used_liquidity_msat,
|
||||
effective_capacity: $candidate.effective_capacity(),
|
||||
};
|
||||
let channel_penalty_msat = scorer.channel_penalty_msat(
|
||||
short_channel_id, &$src_node_id, &$dest_node_id, channel_usage
|
||||
);
|
||||
let path_penalty_msat = $next_hops_path_penalty_msat
|
||||
.saturating_add(channel_penalty_msat);
|
||||
let new_graph_node = RouteGraphNode {
|
||||
node_id: $src_node_id,
|
||||
lowest_fee_to_peer_through_node: total_fee_msat,
|
||||
|
@ -1207,9 +1233,8 @@ where L::Target: Logger {
|
|||
|
||||
// TODO: diversify by nodes (so that all paths aren't doomed if one node is offline).
|
||||
'paths_collection: loop {
|
||||
// For every new path, start from scratch, except
|
||||
// bookkept_channels_liquidity_available_msat, which will improve
|
||||
// the further iterations of path finding. Also don't erase first_hop_targets.
|
||||
// For every new path, start from scratch, except for used_channel_liquidities, which
|
||||
// helps to avoid reusing previously selected paths in future iterations.
|
||||
targets.clear();
|
||||
dist.clear();
|
||||
hit_minimum_limit = false;
|
||||
|
@ -1276,16 +1301,6 @@ where L::Target: Logger {
|
|||
short_channel_id: hop.short_channel_id,
|
||||
})
|
||||
.unwrap_or_else(|| CandidateRouteHop::PrivateHop { hint: hop });
|
||||
let capacity_msat = candidate.effective_capacity().as_msat();
|
||||
aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
|
||||
.saturating_add(scorer.channel_penalty_msat(hop.short_channel_id,
|
||||
final_value_msat, capacity_msat, &source, &target));
|
||||
|
||||
aggregate_next_hops_cltv_delta = aggregate_next_hops_cltv_delta
|
||||
.saturating_add(hop.cltv_expiry_delta as u32);
|
||||
|
||||
aggregate_next_hops_path_length = aggregate_next_hops_path_length
|
||||
.saturating_add(1);
|
||||
|
||||
if !add_entry!(candidate, source, target, aggregate_next_hops_fee_msat,
|
||||
path_value_msat, aggregate_next_hops_path_htlc_minimum_msat,
|
||||
|
@ -1297,6 +1312,25 @@ where L::Target: Logger {
|
|||
hop_used = false;
|
||||
}
|
||||
|
||||
let used_liquidity_msat = used_channel_liquidities
|
||||
.get(&(hop.short_channel_id, source < target)).copied().unwrap_or(0);
|
||||
let channel_usage = ChannelUsage {
|
||||
amount_msat: final_value_msat + aggregate_next_hops_fee_msat,
|
||||
inflight_htlc_msat: used_liquidity_msat,
|
||||
effective_capacity: candidate.effective_capacity(),
|
||||
};
|
||||
let channel_penalty_msat = scorer.channel_penalty_msat(
|
||||
hop.short_channel_id, &source, &target, channel_usage
|
||||
);
|
||||
aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
|
||||
.saturating_add(channel_penalty_msat);
|
||||
|
||||
aggregate_next_hops_cltv_delta = aggregate_next_hops_cltv_delta
|
||||
.saturating_add(hop.cltv_expiry_delta as u32);
|
||||
|
||||
aggregate_next_hops_path_length = aggregate_next_hops_path_length
|
||||
.saturating_add(1);
|
||||
|
||||
// Searching for a direct channel between last checked hop and first_hop_targets
|
||||
if let Some(first_channels) = first_hop_targets.get(&NodeId::from_pubkey(&prev_hop_id)) {
|
||||
for details in first_channels {
|
||||
|
@ -1448,26 +1482,30 @@ where L::Target: Logger {
|
|||
// Remember that we used these channels so that we don't rely
|
||||
// on the same liquidity in future paths.
|
||||
let mut prevented_redundant_path_selection = false;
|
||||
for (payment_hop, _) in payment_path.hops.iter() {
|
||||
let channel_liquidity_available_msat = bookkept_channels_liquidity_available_msat.get_mut(&payment_hop.candidate.short_channel_id()).unwrap();
|
||||
let mut spent_on_hop_msat = value_contribution_msat;
|
||||
let next_hops_fee_msat = payment_hop.next_hops_fee_msat;
|
||||
spent_on_hop_msat += next_hops_fee_msat;
|
||||
if spent_on_hop_msat == *channel_liquidity_available_msat {
|
||||
let prev_hop_iter = core::iter::once(&our_node_id)
|
||||
.chain(payment_path.hops.iter().map(|(hop, _)| &hop.node_id));
|
||||
for (prev_hop, (hop, _)) in prev_hop_iter.zip(payment_path.hops.iter()) {
|
||||
let spent_on_hop_msat = value_contribution_msat + hop.next_hops_fee_msat;
|
||||
let used_liquidity_msat = used_channel_liquidities
|
||||
.entry((hop.candidate.short_channel_id(), *prev_hop < hop.node_id))
|
||||
.and_modify(|used_liquidity_msat| *used_liquidity_msat += spent_on_hop_msat)
|
||||
.or_insert(spent_on_hop_msat);
|
||||
if *used_liquidity_msat == hop.candidate.htlc_maximum_msat() {
|
||||
// If this path used all of this channel's available liquidity, we know
|
||||
// this path will not be selected again in the next loop iteration.
|
||||
prevented_redundant_path_selection = true;
|
||||
}
|
||||
*channel_liquidity_available_msat -= spent_on_hop_msat;
|
||||
debug_assert!(*used_liquidity_msat <= hop.candidate.htlc_maximum_msat());
|
||||
}
|
||||
if !prevented_redundant_path_selection {
|
||||
// If we weren't capped by hitting a liquidity limit on a channel in the path,
|
||||
// we'll probably end up picking the same path again on the next iteration.
|
||||
// Decrease the available liquidity of a hop in the middle of the path.
|
||||
let victim_scid = payment_path.hops[(payment_path.hops.len()) / 2].0.candidate.short_channel_id();
|
||||
let exhausted = u64::max_value();
|
||||
log_trace!(logger, "Disabling channel {} for future path building iterations to avoid duplicates.", victim_scid);
|
||||
let victim_liquidity = bookkept_channels_liquidity_available_msat.get_mut(&victim_scid).unwrap();
|
||||
*victim_liquidity = 0;
|
||||
*used_channel_liquidities.entry((victim_scid, false)).or_default() = exhausted;
|
||||
*used_channel_liquidities.entry((victim_scid, true)).or_default() = exhausted;
|
||||
}
|
||||
|
||||
// Track the total amount all our collected paths allow to send so that we:
|
||||
|
@ -1753,7 +1791,7 @@ mod tests {
|
|||
use routing::router::{get_route, add_random_cltv_offset, default_node_features,
|
||||
PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees,
|
||||
DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE};
|
||||
use routing::scoring::Score;
|
||||
use routing::scoring::{ChannelUsage, Score};
|
||||
use chain::transaction::OutPoint;
|
||||
use chain::keysinterface::KeysInterface;
|
||||
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
|
||||
|
@ -5145,7 +5183,7 @@ mod tests {
|
|||
fn write<W: Writer>(&self, _w: &mut W) -> Result<(), ::io::Error> { unimplemented!() }
|
||||
}
|
||||
impl Score for BadChannelScorer {
|
||||
fn channel_penalty_msat(&self, short_channel_id: u64, _send_amt: u64, _capacity_msat: u64, _source: &NodeId, _target: &NodeId) -> u64 {
|
||||
fn channel_penalty_msat(&self, short_channel_id: u64, _: &NodeId, _: &NodeId, _: ChannelUsage) -> u64 {
|
||||
if short_channel_id == self.short_channel_id { u64::max_value() } else { 0 }
|
||||
}
|
||||
|
||||
|
@ -5163,7 +5201,7 @@ mod tests {
|
|||
}
|
||||
|
||||
impl Score for BadNodeScorer {
|
||||
fn channel_penalty_msat(&self, _short_channel_id: u64, _send_amt: u64, _capacity_msat: u64, _source: &NodeId, target: &NodeId) -> u64 {
|
||||
fn channel_penalty_msat(&self, _: u64, _: &NodeId, target: &NodeId, _: ChannelUsage) -> u64 {
|
||||
if *target == self.node_id { u64::max_value() } else { 0 }
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
//! [`find_route`]: crate::routing::router::find_route
|
||||
|
||||
use ln::msgs::DecodeError;
|
||||
use routing::network_graph::{NetworkGraph, NodeId};
|
||||
use routing::network_graph::{EffectiveCapacity, NetworkGraph, NodeId};
|
||||
use routing::router::RouteHop;
|
||||
use util::ser::{Readable, ReadableArgs, Writeable, Writer};
|
||||
use util::logger::Logger;
|
||||
|
@ -93,7 +93,9 @@ pub trait Score $(: $supertrait)* {
|
|||
/// such as a chain data, network gossip, or invoice hints. For invoice hints, a capacity near
|
||||
/// [`u64::max_value`] is given to indicate sufficient capacity for the invoice's full amount.
|
||||
/// Thus, implementations should be overflow-safe.
|
||||
fn channel_penalty_msat(&self, short_channel_id: u64, send_amt_msat: u64, capacity_msat: u64, source: &NodeId, target: &NodeId) -> u64;
|
||||
fn channel_penalty_msat(
|
||||
&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage
|
||||
) -> u64;
|
||||
|
||||
/// Handles updating channel penalties after failing to route through a channel.
|
||||
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64);
|
||||
|
@ -103,8 +105,10 @@ pub trait Score $(: $supertrait)* {
|
|||
}
|
||||
|
||||
impl<S: Score, T: DerefMut<Target=S> $(+ $supertrait)*> Score for T {
|
||||
fn channel_penalty_msat(&self, short_channel_id: u64, send_amt_msat: u64, capacity_msat: u64, source: &NodeId, target: &NodeId) -> u64 {
|
||||
self.deref().channel_penalty_msat(short_channel_id, send_amt_msat, capacity_msat, source, target)
|
||||
fn channel_penalty_msat(
|
||||
&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage
|
||||
) -> u64 {
|
||||
self.deref().channel_penalty_msat(short_channel_id, source, target, usage)
|
||||
}
|
||||
|
||||
fn payment_path_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
|
||||
|
@ -202,6 +206,20 @@ impl<'a, S: Writeable> Writeable for MutexGuard<'a, S> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Proposed use of a channel passed as a parameter to [`Score::channel_penalty_msat`].
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ChannelUsage {
|
||||
/// The amount to send through the channel, denominated in millisatoshis.
|
||||
pub amount_msat: u64,
|
||||
|
||||
/// Total amount, denominated in millisatoshis, already allocated to send through the channel
|
||||
/// as part of a multi-path payment.
|
||||
pub inflight_htlc_msat: u64,
|
||||
|
||||
/// The effective capacity of the channel.
|
||||
pub effective_capacity: EffectiveCapacity,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
/// [`Score`] implementation that uses a fixed penalty.
|
||||
pub struct FixedPenaltyScorer {
|
||||
|
@ -216,7 +234,7 @@ impl FixedPenaltyScorer {
|
|||
}
|
||||
|
||||
impl Score for FixedPenaltyScorer {
|
||||
fn channel_penalty_msat(&self, _: u64, _: u64, _: u64, _: &NodeId, _: &NodeId) -> u64 {
|
||||
fn channel_penalty_msat(&self, _: u64, _: &NodeId, _: &NodeId, _: ChannelUsage) -> u64 {
|
||||
self.penalty_msat
|
||||
}
|
||||
|
||||
|
@ -407,13 +425,16 @@ impl Default for ScoringParameters {
|
|||
|
||||
impl<T: Time> Score for ScorerUsingTime<T> {
|
||||
fn channel_penalty_msat(
|
||||
&self, short_channel_id: u64, send_amt_msat: u64, capacity_msat: u64, _source: &NodeId, _target: &NodeId
|
||||
&self, short_channel_id: u64, _source: &NodeId, _target: &NodeId, usage: ChannelUsage
|
||||
) -> 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));
|
||||
|
||||
let mut penalty_msat = self.params.base_penalty_msat + failure_penalty_msat;
|
||||
let send_amt_msat = usage.amount_msat;
|
||||
let capacity_msat = usage.effective_capacity.as_msat()
|
||||
.saturating_sub(usage.inflight_htlc_msat);
|
||||
let send_1024ths = send_amt_msat.checked_mul(1024).unwrap_or(u64::max_value()) / capacity_msat;
|
||||
if send_1024ths > self.params.overuse_penalty_start_1024th as u64 {
|
||||
penalty_msat = penalty_msat.checked_add(
|
||||
|
@ -880,10 +901,20 @@ impl<L: DerefMut<Target = u64>, T: Time, U: DerefMut<Target = T>> DirectedChanne
|
|||
|
||||
impl<G: Deref<Target = NetworkGraph>, L: Deref, T: Time> Score for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
|
||||
fn channel_penalty_msat(
|
||||
&self, short_channel_id: u64, amount_msat: u64, capacity_msat: u64, source: &NodeId,
|
||||
target: &NodeId
|
||||
&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage
|
||||
) -> u64 {
|
||||
if let EffectiveCapacity::ExactLiquidity { liquidity_msat } = usage.effective_capacity {
|
||||
if usage.amount_msat > liquidity_msat {
|
||||
return u64::max_value();
|
||||
} else {
|
||||
return self.params.base_penalty_msat;
|
||||
};
|
||||
}
|
||||
|
||||
let liquidity_offset_half_life = self.params.liquidity_offset_half_life;
|
||||
let amount_msat = usage.amount_msat;
|
||||
let capacity_msat = usage.effective_capacity.as_msat()
|
||||
.saturating_sub(usage.inflight_htlc_msat);
|
||||
self.channel_liquidities
|
||||
.get(&short_channel_id)
|
||||
.unwrap_or(&ChannelLiquidity::new())
|
||||
|
@ -1338,8 +1369,8 @@ mod tests {
|
|||
|
||||
use ln::features::{ChannelFeatures, NodeFeatures};
|
||||
use ln::msgs::{ChannelAnnouncement, ChannelUpdate, OptionalField, UnsignedChannelAnnouncement, UnsignedChannelUpdate};
|
||||
use routing::scoring::Score;
|
||||
use routing::network_graph::{NetworkGraph, NodeId};
|
||||
use routing::scoring::{ChannelUsage, Score};
|
||||
use routing::network_graph::{EffectiveCapacity, NetworkGraph, NodeId};
|
||||
use routing::router::RouteHop;
|
||||
use util::ser::{Readable, ReadableArgs, Writeable};
|
||||
use util::test_utils::TestLogger;
|
||||
|
@ -1392,10 +1423,13 @@ mod tests {
|
|||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(1));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1409,16 +1443,19 @@ mod tests {
|
|||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_064);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_064);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_128);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_128);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_192);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_192);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1432,25 +1469,28 @@ mod tests {
|
|||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(9));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(1));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_256);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_256);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10 * 8));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_001);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_001);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1464,18 +1504,21 @@ mod tests {
|
|||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512);
|
||||
|
||||
// An unchecked right shift 64 bits or more in ChannelFailure::decayed_penalty_msat would
|
||||
// cause an overflow.
|
||||
SinceEpoch::advance(Duration::from_secs(10 * 64));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1489,19 +1532,22 @@ mod tests {
|
|||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_256);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_256);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_768);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_768);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_384);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_384);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1515,13 +1561,16 @@ mod tests {
|
|||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_000);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_000);
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_256);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_256);
|
||||
|
||||
let hop = RouteHop {
|
||||
pubkey: PublicKey::from_slice(target.as_slice()).unwrap(),
|
||||
|
@ -1532,10 +1581,10 @@ mod tests {
|
|||
cltv_expiry_delta: 18,
|
||||
};
|
||||
scorer.payment_path_successful(&[&hop]);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_128);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_128);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_064);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_064);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1549,22 +1598,25 @@ mod tests {
|
|||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown
|
||||
};
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_256);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_256);
|
||||
|
||||
scorer.payment_path_failed(&[], 43);
|
||||
assert_eq!(scorer.channel_penalty_msat(43, 1, 1, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(43, &source, &target, usage), 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, 1, 1, &source, &target), 1_256);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(43, 1, 1, &source, &target), 1_512);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 1_256);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(43, &source, &target, usage), 1_512);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1578,9 +1630,12 @@ mod tests {
|
|||
});
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 1, inflight_htlc_msat: 0, effective_capacity: EffectiveCapacity::Unknown
|
||||
};
|
||||
|
||||
scorer.payment_path_failed(&[], 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_512);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_512);
|
||||
|
||||
let mut serialized_scorer = Vec::new();
|
||||
scorer.write(&mut serialized_scorer).unwrap();
|
||||
|
@ -1588,10 +1643,10 @@ 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, 1, 1, &source, &target), 1_256);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 1_256);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 1, 1, &source, &target), 1_128);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 1_128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1606,11 +1661,24 @@ mod tests {
|
|||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1_000, 1_024_000, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 256_999, 1_024_000, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 257_000, 1_024_000, &source, &target), 100);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 258_000, 1_024_000, &source, &target), 200);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512_000, 1_024_000, &source, &target), 256 * 100);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 1_000,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000 },
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
|
||||
let usage = ChannelUsage { amount_msat: 256_999, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
|
||||
let usage = ChannelUsage { amount_msat: 257_000, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 100);
|
||||
|
||||
let usage = ChannelUsage { amount_msat: 258_000, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 200);
|
||||
|
||||
let usage = ChannelUsage { amount_msat: 512_000, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 256 * 100);
|
||||
}
|
||||
|
||||
// `ProbabilisticScorer` tests
|
||||
|
@ -1943,18 +2011,37 @@ mod tests {
|
|||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024_000, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 10_240, 1_024_000, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 102_400, 1_024_000, &source, &target), 47);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1_024_000, 1_024_000, &source, &target), 2_000);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 1_024,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000 },
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 10_240, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 102_400, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 47);
|
||||
let usage = ChannelUsage { amount_msat: 1_024_000, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 58);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 374, 1_024, &source, &target), 198);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 300);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 640, 1_024, &source, &target), 425);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 602);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 902);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 128,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58);
|
||||
let usage = ChannelUsage { amount_msat: 256, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
|
||||
let usage = ChannelUsage { amount_msat: 374, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 198);
|
||||
let usage = ChannelUsage { amount_msat: 512, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
|
||||
let usage = ChannelUsage { amount_msat: 640, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 425);
|
||||
let usage = ChannelUsage { amount_msat: 768, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602);
|
||||
let usage = ChannelUsage { amount_msat: 896, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 902);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1974,10 +2061,17 @@ mod tests {
|
|||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 39, 100, &source, &target), 0);
|
||||
assert_ne!(scorer.channel_penalty_msat(42, 50, 100, &source, &target), 0);
|
||||
assert_ne!(scorer.channel_penalty_msat(42, 50, 100, &source, &target), u64::max_value());
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 61, 100, &source, &target), u64::max_value());
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 39,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 100 },
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 50, ..usage };
|
||||
assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
let usage = ChannelUsage { amount_msat: 61, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1991,16 +2085,21 @@ mod tests {
|
|||
let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
let sender = sender_node_id();
|
||||
let source = source_node_id();
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 500,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
|
||||
};
|
||||
let failed_path = payment_path_for_amount(500);
|
||||
let successful_path = payment_path_for_amount(200);
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 301);
|
||||
assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301);
|
||||
|
||||
scorer.payment_path_failed(&failed_path.iter().collect::<Vec<_>>(), 41);
|
||||
assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 301);
|
||||
assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301);
|
||||
|
||||
scorer.payment_path_successful(&successful_path.iter().collect::<Vec<_>>());
|
||||
assert_eq!(scorer.channel_penalty_msat(41, 500, 1_000, &sender, &source), 301);
|
||||
assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 301);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2016,15 +2115,25 @@ mod tests {
|
|||
let target = target_node_id();
|
||||
let path = payment_path_for_amount(500);
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 128);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 301);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 602);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 250,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
|
||||
let usage = ChannelUsage { amount_msat: 500, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 301);
|
||||
let usage = ChannelUsage { amount_msat: 750, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602);
|
||||
|
||||
scorer.payment_path_failed(&path.iter().collect::<Vec<_>>(), 43);
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 300);
|
||||
let usage = ChannelUsage { amount_msat: 250, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 500, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 750, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2040,15 +2149,25 @@ mod tests {
|
|||
let target = target_node_id();
|
||||
let path = payment_path_for_amount(500);
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 128);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 301);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), 602);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 250,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
|
||||
let usage = ChannelUsage { amount_msat: 500, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 301);
|
||||
let usage = ChannelUsage { amount_msat: 750, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 602);
|
||||
|
||||
scorer.payment_path_failed(&path.iter().collect::<Vec<_>>(), 42);
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 300);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), u64::max_value());
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 750, 1_000, &source, &target), u64::max_value());
|
||||
let usage = ChannelUsage { amount_msat: 250, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
|
||||
let usage = ChannelUsage { amount_msat: 500, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
let usage = ChannelUsage { amount_msat: 750, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2064,17 +2183,22 @@ mod tests {
|
|||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
let recipient = recipient_node_id();
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 250,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
|
||||
};
|
||||
let path = payment_path_for_amount(500);
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 128);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 128);
|
||||
assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 128);
|
||||
assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 128);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 128);
|
||||
assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage), 128);
|
||||
|
||||
scorer.payment_path_successful(&path.iter().collect::<Vec<_>>());
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(41, 250, 1_000, &sender, &source), 128);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 250, 1_000, &source, &target), 300);
|
||||
assert_eq!(scorer.channel_penalty_msat(43, 250, 1_000, &target, &recipient), 300);
|
||||
assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage), 128);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
|
||||
assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage), 300);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2090,44 +2214,70 @@ mod tests {
|
|||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 0, 1_024, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024, &source, &target), 2_000);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 0,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
|
||||
|
||||
scorer.payment_path_failed(&payment_path_for_amount(768).iter().collect::<Vec<_>>(), 42);
|
||||
scorer.payment_path_failed(&payment_path_for_amount(128).iter().collect::<Vec<_>>(), 43);
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 93);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_479);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), u64::max_value());
|
||||
let usage = ChannelUsage { amount_msat: 128, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 256, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 93);
|
||||
let usage = ChannelUsage { amount_msat: 768, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_479);
|
||||
let usage = ChannelUsage { amount_msat: 896, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(9));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 93);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 768, 1_024, &source, &target), 1_479);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), u64::max_value());
|
||||
let usage = ChannelUsage { amount_msat: 128, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 256, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 93);
|
||||
let usage = ChannelUsage { amount_msat: 768, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_479);
|
||||
let usage = ChannelUsage { amount_msat: 896, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(1));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 64, 1_024, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 34);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 896, 1_024, &source, &target), 1_970);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 960, 1_024, &source, &target), u64::max_value());
|
||||
let usage = ChannelUsage { amount_msat: 64, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 128, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 34);
|
||||
let usage = ChannelUsage { amount_msat: 896, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1_970);
|
||||
let usage = ChannelUsage { amount_msat: 960, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
|
||||
// Fully decay liquidity lower bound.
|
||||
SinceEpoch::advance(Duration::from_secs(10 * 7));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 0, 1_024, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1, 1_024, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1_023, 1_024, &source, &target), 2_000);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024, &source, &target), 2_000);
|
||||
let usage = ChannelUsage { amount_msat: 0, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 1, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 1_023, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
|
||||
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
|
||||
|
||||
// Fully decay liquidity upper bound.
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 0, 1_024, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024, &source, &target), 2_000);
|
||||
let usage = ChannelUsage { amount_msat: 0, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 0, 1_024, &source, &target), 0);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 1_024, 1_024, &source, &target), 2_000);
|
||||
let usage = ChannelUsage { amount_msat: 0, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 0);
|
||||
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 2_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2142,18 +2292,23 @@ mod tests {
|
|||
let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 256,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
|
||||
|
||||
scorer.payment_path_failed(&payment_path_for_amount(512).iter().collect::<Vec<_>>(), 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 281);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 281);
|
||||
|
||||
// An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
|
||||
// would cause an overflow.
|
||||
SinceEpoch::advance(Duration::from_secs(10 * 64));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 256, 1_024, &source, &target), 125);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 125);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2168,31 +2323,36 @@ mod tests {
|
|||
let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 512,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
|
||||
};
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 300);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
|
||||
|
||||
// More knowledge gives higher confidence (256, 768), meaning a lower penalty.
|
||||
scorer.payment_path_failed(&payment_path_for_amount(768).iter().collect::<Vec<_>>(), 42);
|
||||
scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::<Vec<_>>(), 43);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 281);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 281);
|
||||
|
||||
// Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 291);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 291);
|
||||
|
||||
// Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
|
||||
// is closer to the upper bound, meaning a higher penalty.
|
||||
scorer.payment_path_successful(&payment_path_for_amount(64).iter().collect::<Vec<_>>());
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 331);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 331);
|
||||
|
||||
// Increasing the lower bound gives more confidence (256, 832) that the payment amount (512)
|
||||
// is closer to the lower bound, meaning a lower penalty.
|
||||
scorer.payment_path_failed(&payment_path_for_amount(256).iter().collect::<Vec<_>>(), 43);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 245);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 245);
|
||||
|
||||
// Further decaying affects the lower bound more than the upper bound (128, 928).
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512, 1_024, &source, &target), 280);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 280);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2207,15 +2367,20 @@ mod tests {
|
|||
let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 500,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
|
||||
};
|
||||
|
||||
scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::<Vec<_>>(), 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), u64::max_value());
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 473);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 473);
|
||||
|
||||
scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::<Vec<_>>(), 43);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
|
||||
|
||||
let mut serialized_scorer = Vec::new();
|
||||
scorer.write(&mut serialized_scorer).unwrap();
|
||||
|
@ -2223,7 +2388,7 @@ mod tests {
|
|||
let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
|
||||
let deserialized_scorer =
|
||||
<ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap();
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 300);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2238,9 +2403,14 @@ mod tests {
|
|||
let mut scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 500,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
|
||||
};
|
||||
|
||||
scorer.payment_path_failed(&payment_path_for_amount(500).iter().collect::<Vec<_>>(), 42);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), u64::max_value());
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
|
||||
let mut serialized_scorer = Vec::new();
|
||||
scorer.write(&mut serialized_scorer).unwrap();
|
||||
|
@ -2250,13 +2420,13 @@ mod tests {
|
|||
let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
|
||||
let deserialized_scorer =
|
||||
<ProbabilisticScorer>::read(&mut serialized_scorer, (params, &network_graph, &logger)).unwrap();
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 473);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 473);
|
||||
|
||||
scorer.payment_path_failed(&payment_path_for_amount(250).iter().collect::<Vec<_>>(), 43);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 300);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
|
||||
|
||||
SinceEpoch::advance(Duration::from_secs(10));
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, 500, 1_000, &source, &target), 365);
|
||||
assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage), 365);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2270,17 +2440,52 @@ mod tests {
|
|||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 950_000_000, &source, &target), 3613);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 1_950_000_000, &source, &target), 1977);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 2_950_000_000, &source, &target), 1474);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 3_950_000_000, &source, &target), 1223);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 4_950_000_000, &source, &target), 877);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 5_950_000_000, &source, &target), 845);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 6_950_000_000, &source, &target), 500);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 7_450_000_000, &source, &target), 500);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 7_950_000_000, &source, &target), 500);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 8_950_000_000, &source, &target), 500);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 100_000_000, 9_950_000_000, &source, &target), 500);
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 100_000_000,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 950_000_000 },
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 3613);
|
||||
let usage = ChannelUsage {
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_950_000_000 }, ..usage
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1977);
|
||||
let usage = ChannelUsage {
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 2_950_000_000 }, ..usage
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1474);
|
||||
let usage = ChannelUsage {
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 3_950_000_000 }, ..usage
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 1223);
|
||||
let usage = ChannelUsage {
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 4_950_000_000 }, ..usage
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 877);
|
||||
let usage = ChannelUsage {
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 5_950_000_000 }, ..usage
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 845);
|
||||
let usage = ChannelUsage {
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 6_950_000_000 }, ..usage
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
|
||||
let usage = ChannelUsage {
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_450_000_000 }, ..usage
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
|
||||
let usage = ChannelUsage {
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_950_000_000 }, ..usage
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
|
||||
let usage = ChannelUsage {
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 8_950_000_000 }, ..usage
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
|
||||
let usage = ChannelUsage {
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 9_950_000_000 }, ..usage
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 500);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2289,19 +2494,24 @@ mod tests {
|
|||
let network_graph = network_graph();
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 128,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024 },
|
||||
};
|
||||
|
||||
let params = ProbabilisticScoringParameters {
|
||||
liquidity_penalty_multiplier_msat: 1_000,
|
||||
..ProbabilisticScoringParameters::zero_penalty()
|
||||
};
|
||||
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 58);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 58);
|
||||
|
||||
let params = ProbabilisticScoringParameters {
|
||||
base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000, ..Default::default()
|
||||
};
|
||||
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 128, 1_024, &source, &target), 558);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 558);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2310,6 +2520,11 @@ mod tests {
|
|||
let network_graph = network_graph();
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 512_000,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000 },
|
||||
};
|
||||
|
||||
let params = ProbabilisticScoringParameters {
|
||||
liquidity_penalty_multiplier_msat: 1_000,
|
||||
|
@ -2317,7 +2532,7 @@ mod tests {
|
|||
..ProbabilisticScoringParameters::zero_penalty()
|
||||
};
|
||||
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512_000, 1_024_000, &source, &target), 300);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 300);
|
||||
|
||||
let params = ProbabilisticScoringParameters {
|
||||
liquidity_penalty_multiplier_msat: 1_000,
|
||||
|
@ -2325,7 +2540,7 @@ mod tests {
|
|||
..ProbabilisticScoringParameters::zero_penalty()
|
||||
};
|
||||
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, 512_000, 1_024_000, &source, &target), 337);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 337);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2334,15 +2549,61 @@ mod tests {
|
|||
let network_graph = network_graph();
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: u64::max_value(),
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Infinite,
|
||||
};
|
||||
|
||||
let params = ProbabilisticScoringParameters {
|
||||
liquidity_penalty_multiplier_msat: 40_000,
|
||||
..ProbabilisticScoringParameters::zero_penalty()
|
||||
};
|
||||
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
assert_eq!(
|
||||
scorer.channel_penalty_msat(42, u64::max_value(), u64::max_value(), &source, &target),
|
||||
80_000,
|
||||
);
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), 80_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accounts_for_inflight_htlc_usage() {
|
||||
let network_graph = network_graph();
|
||||
let logger = TestLogger::new();
|
||||
let params = ProbabilisticScoringParameters::default();
|
||||
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 750,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000 },
|
||||
};
|
||||
assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
|
||||
let usage = ChannelUsage { inflight_htlc_msat: 251, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removes_uncertainity_when_exact_liquidity_known() {
|
||||
let network_graph = network_graph();
|
||||
let logger = TestLogger::new();
|
||||
let params = ProbabilisticScoringParameters::default();
|
||||
let scorer = ProbabilisticScorer::new(params, &network_graph, &logger);
|
||||
let source = source_node_id();
|
||||
let target = target_node_id();
|
||||
|
||||
let base_penalty_msat = params.base_penalty_msat;
|
||||
let usage = ChannelUsage {
|
||||
amount_msat: 750,
|
||||
inflight_htlc_msat: 0,
|
||||
effective_capacity: EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 },
|
||||
};
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), base_penalty_msat);
|
||||
|
||||
let usage = ChannelUsage { amount_msat: 1_000, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), base_penalty_msat);
|
||||
|
||||
let usage = ChannelUsage { amount_msat: 1_001, ..usage };
|
||||
assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage), u64::max_value());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue