Skip to content

Commit 4a21a6c

Browse files
committed
Avoid saturating channels before we split payments
Currently we only opt to split a payment into an MPP if we have completely and totally used a channel's available capacity (up to the announced htlc_max or on-chain capacity, whichever is lower). This is obviously destined to fail as channels are unlikely to have their full capacity available. Here we do the minimum viable fix by simply limiting channels to only using up to a configurable power-of-1/2. We default this new configuration knob to 1 (1/2 of the channel) so as to avoid a substantial change but in the future we may consider changing this to 2 (1/4) or even 3 (1/8).
1 parent 4e5f74a commit 4a21a6c

File tree

2 files changed

+42
-18
lines changed

2 files changed

+42
-18
lines changed

lightning/src/routing/gossip.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -816,10 +816,6 @@ impl<'a> DirectedChannelInfoWithUpdate<'a> {
816816
/// Returns the [`EffectiveCapacity`] of the channel in the direction.
817817
#[inline]
818818
pub(super) fn effective_capacity(&self) -> EffectiveCapacity { self.inner.effective_capacity() }
819-
820-
/// Returns the maximum HTLC amount allowed over the channel in the direction.
821-
#[inline]
822-
pub(super) fn htlc_maximum_msat(&self) -> u64 { self.inner.htlc_maximum_msat() }
823819
}
824820

825821
impl<'a> fmt::Debug for DirectedChannelInfoWithUpdate<'a> {

lightning/src/routing/router.rs

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,16 @@ pub struct PaymentParameters {
225225
/// The maximum number of paths that may be used by (MPP) payments.
226226
/// Defaults to [`DEFAULT_MAX_PATH_COUNT`].
227227
pub max_path_count: u8,
228+
229+
/// Selects the maximum percent of a channel's total capacity which will be sent over a
230+
/// channel, as a power of 1/2. A higher value prefers to send the payment using more MPP parts
231+
/// whereas a lower value prefers to send larger MPP parts, potentially saturating channels.
232+
///
233+
/// A value of 0 will allow payments up to and including a channel's total announced usable
234+
/// capacity, a value of one will only use up to half its capacity, two 1/4, etc.
235+
///
236+
/// Default value: 1
237+
pub max_channel_saturation_power_of_half: u8,
228238
}
229239

230240
impl_writeable_tlv_based!(PaymentParameters, {
@@ -233,6 +243,7 @@ impl_writeable_tlv_based!(PaymentParameters, {
233243
(2, features, option),
234244
(3, max_path_count, (default_value, DEFAULT_MAX_PATH_COUNT)),
235245
(4, route_hints, vec_type),
246+
(5, max_channel_saturation_power_of_half, (default_value, 1)),
236247
(6, expiry_time, option),
237248
});
238249

@@ -246,6 +257,11 @@ impl PaymentParameters {
246257
expiry_time: None,
247258
max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
248259
max_path_count: DEFAULT_MAX_PATH_COUNT,
260+
#[cfg(test)] // Many tests were written prior to the introduction of this parameter, so
261+
// we leave it as 0 by default in tests, and change it for a few.
262+
max_channel_saturation_power_of_half: 0,
263+
#[cfg(not(test))]
264+
max_channel_saturation_power_of_half: 1,
249265
}
250266
}
251267

@@ -433,16 +449,6 @@ impl<'a> CandidateRouteHop<'a> {
433449
}
434450
}
435451

436-
fn htlc_maximum_msat(&self) -> u64 {
437-
match self {
438-
CandidateRouteHop::FirstHop { details } => details.next_outbound_htlc_limit_msat,
439-
CandidateRouteHop::PublicHop { info, .. } => info.htlc_maximum_msat(),
440-
CandidateRouteHop::PrivateHop { hint } => {
441-
hint.htlc_maximum_msat.unwrap_or(u64::max_value())
442-
},
443-
}
444-
}
445-
446452
fn fees(&self) -> RoutingFees {
447453
match self {
448454
CandidateRouteHop::FirstHop { .. } => RoutingFees {
@@ -464,6 +470,24 @@ impl<'a> CandidateRouteHop<'a> {
464470
}
465471
}
466472

473+
#[inline]
474+
fn max_htlc_from_capacity(capacity: EffectiveCapacity, max_channel_saturation_power_of_half: u8) -> u64 {
475+
let saturation_shift: u32 = max_channel_saturation_power_of_half as u32;
476+
match capacity {
477+
EffectiveCapacity::ExactLiquidity { liquidity_msat } => liquidity_msat,
478+
EffectiveCapacity::Infinite => u64::max_value(),
479+
EffectiveCapacity::Unknown => EffectiveCapacity::Unknown.as_msat(),
480+
EffectiveCapacity::MaximumHTLC { amount_msat } =>
481+
amount_msat.checked_shr(saturation_shift).unwrap_or(0),
482+
EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: None } =>
483+
capacity_msat.checked_shr(saturation_shift).unwrap_or(0),
484+
EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: Some(htlc_max) } =>
485+
cmp::min(
486+
capacity_msat.checked_shr(saturation_shift).unwrap_or(0),
487+
htlc_max),
488+
}
489+
}
490+
467491
/// It's useful to keep track of the hops associated with the fees required to use them,
468492
/// so that we can choose cheaper paths (as per Dijkstra's algorithm).
469493
/// Fee values should be updated only in the context of the whole path, see update_value_and_recompute_fees.
@@ -934,7 +958,8 @@ where L::Target: Logger {
934958
// - for first and last hops early in get_route
935959
if $src_node_id != $dest_node_id {
936960
let short_channel_id = $candidate.short_channel_id();
937-
let htlc_maximum_msat = $candidate.htlc_maximum_msat();
961+
let effective_capacity = $candidate.effective_capacity();
962+
let htlc_maximum_msat = max_htlc_from_capacity(effective_capacity, payment_params.max_channel_saturation_power_of_half);
938963

939964
// It is tricky to subtract $next_hops_fee_msat from available liquidity here.
940965
// It may be misleading because we might later choose to reduce the value transferred
@@ -1084,7 +1109,7 @@ where L::Target: Logger {
10841109
let channel_usage = ChannelUsage {
10851110
amount_msat: amount_to_transfer_over_msat,
10861111
inflight_htlc_msat: used_liquidity_msat,
1087-
effective_capacity: $candidate.effective_capacity(),
1112+
effective_capacity,
10881113
};
10891114
let channel_penalty_msat = scorer.channel_penalty_msat(
10901115
short_channel_id, &$src_node_id, &$dest_node_id, channel_usage
@@ -1505,12 +1530,15 @@ where L::Target: Logger {
15051530
.entry((hop.candidate.short_channel_id(), *prev_hop < hop.node_id))
15061531
.and_modify(|used_liquidity_msat| *used_liquidity_msat += spent_on_hop_msat)
15071532
.or_insert(spent_on_hop_msat);
1508-
if *used_liquidity_msat == hop.candidate.htlc_maximum_msat() {
1533+
let hop_capacity = hop.candidate.effective_capacity();
1534+
let hop_max_msat = max_htlc_from_capacity(hop_capacity,
1535+
payment_params.max_channel_saturation_power_of_half);
1536+
if *used_liquidity_msat == hop_max_msat {
15091537
// If this path used all of this channel's available liquidity, we know
15101538
// this path will not be selected again in the next loop iteration.
15111539
prevented_redundant_path_selection = true;
15121540
}
1513-
debug_assert!(*used_liquidity_msat <= hop.candidate.htlc_maximum_msat());
1541+
debug_assert!(*used_liquidity_msat <= hop_max_msat);
15141542
}
15151543
if !prevented_redundant_path_selection {
15161544
// If we weren't capped by hitting a liquidity limit on a channel in the path,

0 commit comments

Comments
 (0)