Merge pull request #1456 from jkczyz/2022-04-effective-capacity

Use `EffectiveCapacity` in `Score` trait
This commit is contained in:
Arik Sosman 2022-05-20 10:56:10 -07:00 committed by GitHub
commit 0c6974b602
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 530 additions and 207 deletions

View file

@ -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) {

View file

@ -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.

View file

@ -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 }
}

View file

@ -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());
}
}