From: Matt Corallo Date: Sat, 16 Apr 2022 20:07:34 +0000 (+0000) Subject: Separate `ChannelDetails`' outbound capacity from the next HTLC max X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=71b68a1df2942e844f41decf21a021dc49b83a40;p=rust-lightning 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. --- diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 9ccdf816a..cac6dfd2a 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -2330,23 +2330,30 @@ impl Channel { stats } - /// Get the available (ie not including pending HTLCs) inbound and outbound balance in msat. + /// Get the available (ie not including pending HTLCs) inbound and outbound balance, plus the + /// amount available for a single HTLC send, all in msat. /// Doesn't bother handling the /// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC /// corner case properly. /// The channel reserve is subtracted from each balance. /// See also [`Channel::get_balance_msat`] - pub fn get_inbound_outbound_available_balance_msat(&self) -> (u64, u64) { + pub fn get_inbound_outbound_available_balance_msat(&self) -> (u64, u64, u64) { // Note that we have to handle overflow due to the above case. + let outbound_stats = self.get_outbound_pending_htlc_stats(None); + let outbound_capacity_msat = cmp::max(self.value_to_self_msat as i64 + - outbound_stats.pending_htlcs_value_msat as i64 + - self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000, + 0) as u64; ( cmp::max(self.channel_value_satoshis as i64 * 1000 - self.value_to_self_msat as i64 - self.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64 - self.holder_selected_channel_reserve_satoshis as i64 * 1000, 0) as u64, - cmp::max(self.value_to_self_msat as i64 - - self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64 - - self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000, + outbound_capacity_msat, + cmp::max(cmp::min(outbound_capacity_msat as i64, + self.counterparty_max_htlc_value_in_flight_msat as i64 + - outbound_stats.pending_htlcs_value_msat as i64), 0) as u64 ) } diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 7323930f3..f7e192f60 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -999,6 +999,13 @@ pub struct ChannelDetails { /// conflict-avoidance policy, exactly this amount is not likely to be spendable. However, we /// should be able to spend nearly this amount. pub outbound_capacity_msat: u64, + /// The available outbound capacity for sending a single HTLC to the remote peer. This is + /// similar to [`ChannelDetails::outbound_capacity_msat`] but it may be further restricted by + /// the current state and per-HTLC limit(s). This is intended for use when routing, allowing us + /// to use a limit as close as possible to the HTLC limit we can currently send. + /// + /// See also [`ChannelDetails::balance_msat`] and [`ChannelDetails::outbound_capacity_msat`]. + pub outbound_htlc_limit_msat: u64, /// The available inbound capacity for the remote peer to send HTLCs to us. This does not /// include any pending HTLCs which are not yet fully resolved (and, thus, whose balance is not /// available for inclusion in new inbound HTLCs). @@ -1659,7 +1666,8 @@ impl ChannelMana let channel_state = self.channel_state.lock().unwrap(); res.reserve(channel_state.by_id.len()); for (channel_id, channel) in channel_state.by_id.iter().filter(f) { - let (inbound_capacity_msat, outbound_capacity_msat) = channel.get_inbound_outbound_available_balance_msat(); + let (inbound_capacity_msat, outbound_capacity_msat, outbound_htlc_limit_msat) = + channel.get_inbound_outbound_available_balance_msat(); let balance_msat = channel.get_balance_msat(); let (to_remote_reserve_satoshis, to_self_reserve_satoshis) = channel.get_holder_counterparty_selected_channel_reserve_satoshis(); @@ -1682,6 +1690,7 @@ impl ChannelMana balance_msat, inbound_capacity_msat, outbound_capacity_msat, + outbound_htlc_limit_msat, user_channel_id: channel.get_user_id(), confirmations_required: channel.minimum_depth(), force_close_spend_delay: channel.get_counterparty_selected_contest_delay(), @@ -5911,6 +5920,9 @@ impl_writeable_tlv_based!(ChannelDetails, { (14, user_channel_id, required), (16, balance_msat, required), (18, outbound_capacity_msat, required), + // Note that by the time we get past the required read above, outbound_capacity_msat will be + // filled in, so we can safely unwrap it here. + (19, outbound_htlc_limit_msat, (default_value, outbound_capacity_msat.0.unwrap())), (20, inbound_capacity_msat, required), (22, confirmations_required, option), (24, force_close_spend_delay, option), diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index 6584eb5fb..7a6d3736e 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -412,7 +412,7 @@ impl<'a> CandidateRouteHop<'a> { fn effective_capacity(&self) -> EffectiveCapacity { match self { CandidateRouteHop::FirstHop { details } => EffectiveCapacity::ExactLiquidity { - liquidity_msat: details.outbound_capacity_msat, + liquidity_msat: details.outbound_htlc_limit_msat, }, CandidateRouteHop::PublicHop { info, .. } => info.effective_capacity(), CandidateRouteHop::PrivateHop { .. } => EffectiveCapacity::Infinite, @@ -1732,6 +1732,7 @@ mod tests { user_channel_id: 0, balance_msat: 0, outbound_capacity_msat, + outbound_htlc_limit_msat: outbound_capacity_msat, inbound_capacity_msat: 42, unspendable_punishment_reserve: None, confirmations_required: None,