Skip to content

Commit ba4bb6e

Browse files
committed
Separate ChannelDetails' outbound capacity from the next HTLC max
`ChannelDetails::outbound_capacity_msat` describes the total amount available for sending across several HTLCs, basically just our balance minus the reserve value maintained by our counterparty. However, when routing we use it to guess the maximum amount we can send in a single additional HTLC, which it is not. There are numerous reasons why our balance may not match the amount we can send in a single HTLC, whether the HTLC in-flight limit, the channe's HTLC maximum, or our feerate buffer. This commit splits the `outbound_capacity_msat` field into two - `outbound_capacity_msat` and `outbound_htlc_limit_msat`, setting us up for correctly handling our next-HTLC-limit in the future. This also addresses the first of the reasons why the values may not match - the max-in-flight limit. The inaccuracy is ultimately tracked as #1126.
1 parent e0b9b74 commit ba4bb6e

File tree

4 files changed

+34
-10
lines changed

4 files changed

+34
-10
lines changed

fuzz/src/router.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use bitcoin::blockdata::constants::genesis_block;
2929

3030
use utils::test_logger;
3131

32+
use std::convert::TryInto;
3233
use std::collections::HashSet;
3334
use std::sync::Arc;
3435
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -205,7 +206,8 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
205206
count => {
206207
for _ in 0..count {
207208
scid += 1;
208-
let rnid = node_pks.iter().skip(slice_to_be16(get_slice!(2))as usize % node_pks.len()).next().unwrap();
209+
let rnid = node_pks.iter().skip(u16::from_be_bytes(get_slice!(2).try_into().unwrap()) as usize % node_pks.len()).next().unwrap();
210+
let capacity = u64::from_be_bytes(get_slice!(8).try_into().unwrap());
209211
first_hops_vec.push(ChannelDetails {
210212
channel_id: [0; 32],
211213
counterparty: ChannelCounterparty {
@@ -218,15 +220,16 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
218220
channel_type: None,
219221
short_channel_id: Some(scid),
220222
inbound_scid_alias: None,
221-
channel_value_satoshis: slice_to_be64(get_slice!(8)),
223+
channel_value_satoshis: capacity,
222224
user_channel_id: 0, inbound_capacity_msat: 0,
223225
unspendable_punishment_reserve: None,
224226
confirmations_required: None,
225227
force_close_spend_delay: None,
226228
is_outbound: true, is_funding_locked: true,
227229
is_usable: true, is_public: true,
228230
balance_msat: 0,
229-
outbound_capacity_msat: 0,
231+
outbound_capacity_msat: capacity * 1000,
232+
outbound_htlc_limit_msat: capacity * 1000
230233
});
231234
}
232235
Some(&first_hops_vec[..])

lightning/src/ln/channel.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2330,23 +2330,30 @@ impl<Signer: Sign> Channel<Signer> {
23302330
stats
23312331
}
23322332

2333-
/// Get the available (ie not including pending HTLCs) inbound and outbound balance in msat.
2333+
/// Get the available (ie not including pending HTLCs) inbound and outbound balance, plus the
2334+
/// amount available for a single HTLC send, all in msat.
23342335
/// Doesn't bother handling the
23352336
/// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
23362337
/// corner case properly.
23372338
/// The channel reserve is subtracted from each balance.
23382339
/// See also [`Channel::get_balance_msat`]
2339-
pub fn get_inbound_outbound_available_balance_msat(&self) -> (u64, u64) {
2340+
pub fn get_inbound_outbound_available_balance_msat(&self) -> (u64, u64, u64) {
23402341
// Note that we have to handle overflow due to the above case.
2342+
let outbound_stats = self.get_outbound_pending_htlc_stats(None);
2343+
let outbound_capacity_msat = cmp::max(self.value_to_self_msat as i64
2344+
- outbound_stats.pending_htlcs_value_msat as i64
2345+
- self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
2346+
0) as u64;
23412347
(
23422348
cmp::max(self.channel_value_satoshis as i64 * 1000
23432349
- self.value_to_self_msat as i64
23442350
- self.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
23452351
- self.holder_selected_channel_reserve_satoshis as i64 * 1000,
23462352
0) as u64,
2347-
cmp::max(self.value_to_self_msat as i64
2348-
- self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
2349-
- self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
2353+
outbound_capacity_msat,
2354+
cmp::max(cmp::min(outbound_capacity_msat as i64,
2355+
self.counterparty_max_htlc_value_in_flight_msat as i64
2356+
- outbound_stats.pending_htlcs_value_msat as i64),
23502357
0) as u64
23512358
)
23522359
}

lightning/src/ln/channelmanager.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,13 @@ pub struct ChannelDetails {
999999
/// conflict-avoidance policy, exactly this amount is not likely to be spendable. However, we
10001000
/// should be able to spend nearly this amount.
10011001
pub outbound_capacity_msat: u64,
1002+
/// The available outbound capacity for sending a single HTLC to the remote peer. This is
1003+
/// similar to [`ChannelDetails::outbound_capacity_msat`] but it may be further restricted by
1004+
/// the current state and per-HTLC limit(s). This is intended for use when routing, allowing us
1005+
/// to use a limit as close as possible to the HTLC limit we can currently send.
1006+
///
1007+
/// See also [`ChannelDetails::balance_msat`] and [`ChannelDetails::outbound_capacity_msat`].
1008+
pub outbound_htlc_limit_msat: u64,
10021009
/// The available inbound capacity for the remote peer to send HTLCs to us. This does not
10031010
/// include any pending HTLCs which are not yet fully resolved (and, thus, whose balance is not
10041011
/// available for inclusion in new inbound HTLCs).
@@ -1659,7 +1666,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
16591666
let channel_state = self.channel_state.lock().unwrap();
16601667
res.reserve(channel_state.by_id.len());
16611668
for (channel_id, channel) in channel_state.by_id.iter().filter(f) {
1662-
let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat();
1669+
let (inbound_capacity_msat, outbound_capacity_msat, outbound_htlc_limit_msat) =
1670+
channel.get_inbound_outbound_available_balance_msat();
16631671
let balance_msat = channel.get_balance_msat();
16641672
let (to_remote_reserve_satoshis, to_self_reserve_satoshis) =
16651673
channel.get_holder_counterparty_selected_channel_reserve_satoshis();
@@ -1682,6 +1690,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
16821690
balance_msat,
16831691
inbound_capacity_msat,
16841692
outbound_capacity_msat,
1693+
outbound_htlc_limit_msat,
16851694
user_channel_id: channel.get_user_id(),
16861695
confirmations_required: channel.minimum_depth(),
16871696
force_close_spend_delay: channel.get_counterparty_selected_contest_delay(),
@@ -5911,6 +5920,9 @@ impl_writeable_tlv_based!(ChannelDetails, {
59115920
(14, user_channel_id, required),
59125921
(16, balance_msat, required),
59135922
(18, outbound_capacity_msat, required),
5923+
// Note that by the time we get past the required read above, outbound_capacity_msat will be
5924+
// filled in, so we can safely unwrap it here.
5925+
(19, outbound_htlc_limit_msat, (default_value, outbound_capacity_msat.0.unwrap())),
59145926
(20, inbound_capacity_msat, required),
59155927
(22, confirmations_required, option),
59165928
(24, force_close_spend_delay, option),

lightning/src/routing/router.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ impl<'a> CandidateRouteHop<'a> {
412412
fn effective_capacity(&self) -> EffectiveCapacity {
413413
match self {
414414
CandidateRouteHop::FirstHop { details } => EffectiveCapacity::ExactLiquidity {
415-
liquidity_msat: details.outbound_capacity_msat,
415+
liquidity_msat: details.outbound_htlc_limit_msat,
416416
},
417417
CandidateRouteHop::PublicHop { info, .. } => info.effective_capacity(),
418418
CandidateRouteHop::PrivateHop { .. } => EffectiveCapacity::Infinite,
@@ -1744,6 +1744,7 @@ mod tests {
17441744
user_channel_id: 0,
17451745
balance_msat: 0,
17461746
outbound_capacity_msat,
1747+
outbound_htlc_limit_msat: outbound_capacity_msat,
17471748
inbound_capacity_msat: 42,
17481749
unspendable_punishment_reserve: None,
17491750
confirmations_required: None,
@@ -5467,6 +5468,7 @@ mod benches {
54675468
user_channel_id: 0,
54685469
balance_msat: 10_000_000,
54695470
outbound_capacity_msat: 10_000_000,
5471+
outbound_htlc_limit_msat: 10_000_000,
54705472
inbound_capacity_msat: 0,
54715473
unspendable_punishment_reserve: None,
54725474
confirmations_required: None,

0 commit comments

Comments
 (0)