Skip to content

Commit bd6b710

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 bd6b710

File tree

2 files changed

+41
-18
lines changed

2 files changed

+41
-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: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,17 @@ 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 share of a channel's total capacity which will be sent over a channel,
230+
/// as a power of 1/2. A higher value prefers to send the payment using more MPP parts whereas
231+
/// a lower value prefers to send larger MPP parts, potentially saturating channels and
232+
/// increasing failure probability for those paths.
233+
///
234+
/// A value of 0 will allow payments up to and including a channel's total announced usable
235+
/// capacity, a value of one will only use up to half its capacity, two 1/4, etc.
236+
///
237+
/// Default value: 1
238+
pub max_channel_saturation_power_of_half: u8,
228239
}
229240

230241
impl_writeable_tlv_based!(PaymentParameters, {
@@ -233,6 +244,7 @@ impl_writeable_tlv_based!(PaymentParameters, {
233244
(2, features, option),
234245
(3, max_path_count, (default_value, DEFAULT_MAX_PATH_COUNT)),
235246
(4, route_hints, vec_type),
247+
(5, max_channel_saturation_power_of_half, (default_value, 1)),
236248
(6, expiry_time, option),
237249
});
238250

@@ -246,6 +258,11 @@ impl PaymentParameters {
246258
expiry_time: None,
247259
max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
248260
max_path_count: DEFAULT_MAX_PATH_COUNT,
261+
#[cfg(test)] // Many tests were written prior to the introduction of this parameter, so
262+
// we leave it as 0 by default in tests, and change it for a few.
263+
max_channel_saturation_power_of_half: 0,
264+
#[cfg(not(test))]
265+
max_channel_saturation_power_of_half: 1,
249266
}
250267
}
251268

@@ -433,16 +450,6 @@ impl<'a> CandidateRouteHop<'a> {
433450
}
434451
}
435452

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-
446453
fn fees(&self) -> RoutingFees {
447454
match self {
448455
CandidateRouteHop::FirstHop { .. } => RoutingFees {
@@ -464,6 +471,22 @@ impl<'a> CandidateRouteHop<'a> {
464471
}
465472
}
466473

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

939963
// It is tricky to subtract $next_hops_fee_msat from available liquidity here.
940964
// It may be misleading because we might later choose to reduce the value transferred
@@ -1084,7 +1108,7 @@ where L::Target: Logger {
10841108
let channel_usage = ChannelUsage {
10851109
amount_msat: amount_to_transfer_over_msat,
10861110
inflight_htlc_msat: used_liquidity_msat,
1087-
effective_capacity: $candidate.effective_capacity(),
1111+
effective_capacity,
10881112
};
10891113
let channel_penalty_msat = scorer.channel_penalty_msat(
10901114
short_channel_id, &$src_node_id, &$dest_node_id, channel_usage
@@ -1505,12 +1529,15 @@ where L::Target: Logger {
15051529
.entry((hop.candidate.short_channel_id(), *prev_hop < hop.node_id))
15061530
.and_modify(|used_liquidity_msat| *used_liquidity_msat += spent_on_hop_msat)
15071531
.or_insert(spent_on_hop_msat);
1508-
if *used_liquidity_msat == hop.candidate.htlc_maximum_msat() {
1532+
let hop_capacity = hop.candidate.effective_capacity();
1533+
let hop_max_msat = max_htlc_from_capacity(hop_capacity,
1534+
payment_params.max_channel_saturation_power_of_half);
1535+
if *used_liquidity_msat == hop_max_msat {
15091536
// If this path used all of this channel's available liquidity, we know
15101537
// this path will not be selected again in the next loop iteration.
15111538
prevented_redundant_path_selection = true;
15121539
}
1513-
debug_assert!(*used_liquidity_msat <= hop.candidate.htlc_maximum_msat());
1540+
debug_assert!(*used_liquidity_msat <= hop_max_msat);
15141541
}
15151542
if !prevented_redundant_path_selection {
15161543
// If we weren't capped by hitting a liquidity limit on a channel in the path,

0 commit comments

Comments
 (0)