Skip to content

Commit 9631550

Browse files
committed
Improve prediction of commitment stats in validate_update_add_htlc
`ChannelContext::get_pending_htlc_stats` predicts that the set of HTLCs on the next commitment will be all the HTLCs in `ChannelContext.pending_inbound_htlcs`, and `ChannelContext.pending_outbound_htlcs`, as well as all the outbound HTLC adds in the holding cell. This is an overestimate: * Outbound HTLC removals which have been ACK'ed by the counterparty will certainly not be present in any *next* commitment, even though they remain in `pending_outbound_htlcs`. * Outbound HTLCs in the `RemoteRemoved` state, will not be present in the next *local* commitment. * Outbound HTLCs in the `LocalAnnounced` state have no guarantee that they were received by the counterparty before she sent the `update_fee`. * Outbound `update_add_htlc`'s in the holding cell are certainly not known by the counterparty, and we will reevaluate their addition to the channel when freeing the holding cell. * Inbound HTLCs in the `LocalRemoved` state will not be present in the next *remote* commitment. `ChannelContext::next_local_commit_tx_fee_msat` over-counts outbound HTLCs in the `LocalAnnounced` and `RemoteRemoved` states, as well as outbound `update_add_htlc`'s in the holding cell. `ChannelContext::next_remote_commit_tx_fee_msat` over-counts inbound HTLCs in the `LocalRemoved` state, as well as outbound HTLCs in the `LocalAnnounced` state. This commit stops using these functions in favor of the newly added `ChannelContext::get_next_{local, remote}_commitment_stats` methods, and fixes the issues described above. As a side-effect, this commit makes consistent the set of HTLCs used to calculate dust exposure, transaction fees, and balances in `validate_update_add_htlc`.
1 parent 48ad0e5 commit 9631550

File tree

1 file changed

+18
-43
lines changed

1 file changed

+18
-43
lines changed

lightning/src/ln/channel.rs

Lines changed: 18 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,12 +1097,12 @@ pub enum AnnouncementSigsState {
10971097
/// An enum indicating whether the local or remote side offered a given HTLC.
10981098
enum HTLCInitiator {
10991099
LocalOffered,
1100+
#[allow(dead_code)]
11001101
RemoteOffered,
11011102
}
11021103

11031104
/// Current counts of various HTLCs, useful for calculating current balances available exactly.
11041105
struct HTLCStats {
1105-
pending_inbound_htlcs: usize,
11061106
pending_outbound_htlcs: usize,
11071107
pending_inbound_htlcs_value_msat: u64,
11081108
pending_outbound_htlcs_value_msat: u64,
@@ -4111,7 +4111,6 @@ where
41114111
///
41124112
/// We take the conservative approach and only assume that a HTLC will
41134113
/// not be in the next commitment when it is guaranteed that it won't be.
4114-
#[allow(dead_code)]
41154114
#[rustfmt::skip]
41164115
fn get_next_commitment_htlcs(
41174116
&self, local: bool, htlc_candidate: Option<HTLCAmountDirection>, include_counterparty_unknown_htlcs: bool,
@@ -4181,7 +4180,6 @@ where
41814180
/// will *not* be present on the next commitment from `next_commitment_htlcs`, and
41824181
/// check if their outcome is successful. If it is, we add the value of this claimed
41834182
/// HTLC to the balance of the claimer.
4184-
#[allow(dead_code)]
41854183
#[rustfmt::skip]
41864184
fn get_next_commitment_value_to_self_msat(&self, local: bool, funding: &FundingScope) -> u64 {
41874185
let inbound_claimed_htlc_msat: u64 =
@@ -4213,7 +4211,6 @@ where
42134211
.saturating_add(inbound_claimed_htlc_msat)
42144212
}
42154213

4216-
#[allow(dead_code)]
42174214
fn get_next_local_commitment_stats(
42184215
&self, funding: &FundingScope, htlc_candidate: Option<HTLCAmountDirection>,
42194216
include_counterparty_unknown_htlcs: bool, addl_nondust_htlc_count: usize,
@@ -4239,7 +4236,6 @@ where
42394236
)
42404237
}
42414238

4242-
#[allow(dead_code)]
42434239
fn get_next_remote_commitment_stats(
42444240
&self, funding: &FundingScope, htlc_candidate: Option<HTLCAmountDirection>,
42454241
include_counterparty_unknown_htlcs: bool, addl_nondust_htlc_count: usize,
@@ -4280,15 +4276,23 @@ where
42804276
let dust_exposure_limiting_feerate = self.get_dust_exposure_limiting_feerate(
42814277
&fee_estimator, funding.get_channel_type(),
42824278
);
4283-
let htlc_stats = self.get_pending_htlc_stats(funding, None, dust_exposure_limiting_feerate);
4284-
if htlc_stats.pending_inbound_htlcs + 1 > self.holder_max_accepted_htlcs as usize {
4279+
// Do not include outbound update_add_htlc's in the holding cell, or those which haven't yet been ACK'ed by the counterparty (ie. LocalAnnounced HTLCs)
4280+
let do_not_include_counterparty_unknown_htlcs = false;
4281+
let next_remote_commitment_stats = self.get_next_remote_commitment_stats(funding, Some(HTLCAmountDirection { outbound: false, amount_msat: msg.amount_msat }), do_not_include_counterparty_unknown_htlcs, 0, self.feerate_per_kw, dust_exposure_limiting_feerate);
4282+
4283+
if next_remote_commitment_stats.inbound_htlcs_count > self.holder_max_accepted_htlcs as usize {
42854284
return Err(ChannelError::close(format!("Remote tried to push more than our max accepted HTLCs ({})", self.holder_max_accepted_htlcs)));
42864285
}
4287-
if htlc_stats.pending_inbound_htlcs_value_msat + msg.amount_msat > self.holder_max_htlc_value_in_flight_msat {
4286+
if next_remote_commitment_stats.inbound_htlcs_value_msat > self.holder_max_htlc_value_in_flight_msat {
42884287
return Err(ChannelError::close(format!("Remote HTLC add would put them over our max HTLC value ({})", self.holder_max_htlc_value_in_flight_msat)));
42894288
}
42904289

4291-
// Check holder_selected_channel_reserve_satoshis (we're getting paid, so they have to at least meet
4290+
let remote_balance_before_fee_msat = next_remote_commitment_stats.counterparty_balance_msat.ok_or(ChannelError::close("Remote HTLC add would overdraw remaining funds".to_owned()))?;
4291+
4292+
// Check that the remote can afford to pay for this HTLC on-chain at the current
4293+
// feerate_per_kw, while maintaining their channel reserve (as required by the spec).
4294+
//
4295+
// We check holder_selected_channel_reserve_satoshis (we're getting paid, so they have to at least meet
42924296
// the reserve_satoshis we told them to always have as direct payment so that they lose
42934297
// something if we punish them for broadcasting an old state).
42944298
// Note that we don't really care about having a small/no to_remote output in our local
@@ -4300,50 +4304,22 @@ where
43004304
// violate the reserve value if we do not do this (as we forget inbound HTLCs from the
43014305
// Channel state once they will not be present in the next received commitment
43024306
// transaction).
4303-
let (local_balance_before_fee_msat, remote_balance_before_fee_msat) = {
4304-
let removed_outbound_total_msat: u64 = self.pending_outbound_htlcs
4305-
.iter()
4306-
.filter_map(|htlc| {
4307-
matches!(
4308-
htlc.state,
4309-
OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_, _))
4310-
| OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_, _))
4311-
)
4312-
.then_some(htlc.amount_msat)
4313-
})
4314-
.sum();
4315-
let pending_value_to_self_msat =
4316-
funding.value_to_self_msat + htlc_stats.pending_inbound_htlcs_value_msat - removed_outbound_total_msat;
4317-
let pending_remote_value_msat =
4318-
funding.get_value_satoshis() * 1000 - pending_value_to_self_msat;
4319-
4320-
// Subtract any non-HTLC outputs from the local and remote balances
4321-
SpecTxBuilder {}.subtract_non_htlc_outputs(funding.is_outbound(), funding.value_to_self_msat, pending_remote_value_msat, funding.get_channel_type())
4322-
};
4323-
if remote_balance_before_fee_msat < msg.amount_msat {
4324-
return Err(ChannelError::close("Remote HTLC add would overdraw remaining funds".to_owned()));
4325-
}
4326-
4327-
// Check that the remote can afford to pay for this HTLC on-chain at the current
4328-
// feerate_per_kw, while maintaining their channel reserve (as required by the spec).
43294307
{
43304308
let remote_commit_tx_fee_msat = if funding.is_outbound() { 0 } else {
4331-
let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
4332-
self.next_remote_commit_tx_fee_msat(funding, Some(htlc_candidate), None) // Don't include the extra fee spike buffer HTLC in calculations
4309+
next_remote_commitment_stats.commit_tx_fee_sat * 1000
43334310
};
4334-
if remote_balance_before_fee_msat.saturating_sub(msg.amount_msat) < remote_commit_tx_fee_msat {
4311+
if remote_balance_before_fee_msat < remote_commit_tx_fee_msat {
43354312
return Err(ChannelError::close("Remote HTLC add would not leave enough to pay for fees".to_owned()));
43364313
};
4337-
if remote_balance_before_fee_msat.saturating_sub(msg.amount_msat).saturating_sub(remote_commit_tx_fee_msat) < funding.holder_selected_channel_reserve_satoshis * 1000 {
4314+
if remote_balance_before_fee_msat.saturating_sub(remote_commit_tx_fee_msat) < funding.holder_selected_channel_reserve_satoshis * 1000 {
43384315
return Err(ChannelError::close("Remote HTLC add would put them under remote reserve value".to_owned()));
43394316
}
43404317
}
43414318

4319+
let next_local_commitment_stats = self.get_next_local_commitment_stats(funding, Some(HTLCAmountDirection { outbound: false, amount_msat: msg.amount_msat }), do_not_include_counterparty_unknown_htlcs, 0, self.feerate_per_kw, dust_exposure_limiting_feerate);
43424320
if funding.is_outbound() {
43434321
// Check that they won't violate our local required channel reserve by adding this HTLC.
4344-
let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
4345-
let local_commit_tx_fee_msat = self.next_local_commit_tx_fee_msat(funding, htlc_candidate, None);
4346-
if local_balance_before_fee_msat < funding.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 + local_commit_tx_fee_msat {
4322+
if next_local_commitment_stats.holder_balance_msat.unwrap_or(0) < funding.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 + next_local_commitment_stats.commit_tx_fee_sat * 1000 {
43474323
return Err(ChannelError::close("Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value".to_owned()));
43484324
}
43494325
}
@@ -4956,7 +4932,6 @@ where
49564932
});
49574933

49584934
HTLCStats {
4959-
pending_inbound_htlcs: self.pending_inbound_htlcs.len(),
49604935
pending_outbound_htlcs,
49614936
pending_inbound_htlcs_value_msat,
49624937
pending_outbound_htlcs_value_msat,

0 commit comments

Comments
 (0)