Skip to content

Commit b7783ad

Browse files
committed
Check pending funding in can_accept_incoming_htlc
If there are any pending splices when an accepting an incoming HTLC, the HTLC needs to be validated against each pending FundingScope. Otherwise, once the splice is locked, the HTLC could have been failed when it should have been forwarded / claimed, or vice versa, under the promoted FundingScope.
1 parent 131cd25 commit b7783ad

File tree

1 file changed

+23
-10
lines changed

1 file changed

+23
-10
lines changed

lightning/src/ln/channel.rs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8134,7 +8134,20 @@ impl<SP: Deref> FundedChannel<SP> where
81348134
}
81358135

81368136
let dust_exposure_limiting_feerate = self.context.get_dust_exposure_limiting_feerate(&fee_estimator);
8137-
let htlc_stats = self.context.get_pending_htlc_stats(&self.funding, None, dust_exposure_limiting_feerate);
8137+
8138+
core::iter::once(&self.funding)
8139+
.chain(self.pending_funding.iter())
8140+
.try_for_each(|funding| self.can_accept_incoming_htlc_for_funding(funding, msg, dust_exposure_limiting_feerate, &logger))
8141+
}
8142+
8143+
fn can_accept_incoming_htlc_for_funding<L: Deref>(
8144+
&self, funding: &FundingScope, msg: &msgs::UpdateAddHTLC,
8145+
dust_exposure_limiting_feerate: u32, logger: &L,
8146+
) -> Result<(), LocalHTLCFailureReason>
8147+
where
8148+
L::Target: Logger,
8149+
{
8150+
let htlc_stats = self.context.get_pending_htlc_stats(funding, None, dust_exposure_limiting_feerate);
81388151
let max_dust_htlc_exposure_msat = self.context.get_max_dust_htlc_exposure_msat(dust_exposure_limiting_feerate);
81398152
let on_counterparty_tx_dust_htlc_exposure_msat = htlc_stats.on_counterparty_tx_dust_exposure_msat;
81408153
if on_counterparty_tx_dust_htlc_exposure_msat > max_dust_htlc_exposure_msat {
@@ -8143,11 +8156,11 @@ impl<SP: Deref> FundedChannel<SP> where
81438156
on_counterparty_tx_dust_htlc_exposure_msat, max_dust_htlc_exposure_msat);
81448157
return Err(LocalHTLCFailureReason::DustLimitCounterparty)
81458158
}
8146-
let htlc_success_dust_limit = if self.funding.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
8159+
let htlc_success_dust_limit = if funding.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
81478160
0
81488161
} else {
81498162
let dust_buffer_feerate = self.context.get_dust_buffer_feerate(None) as u64;
8150-
dust_buffer_feerate * htlc_success_tx_weight(self.funding.get_channel_type()) / 1000
8163+
dust_buffer_feerate * htlc_success_tx_weight(funding.get_channel_type()) / 1000
81518164
};
81528165
let exposure_dust_limit_success_sats = htlc_success_dust_limit + self.context.holder_dust_limit_satoshis;
81538166
if msg.amount_msat / 1000 < exposure_dust_limit_success_sats {
@@ -8159,7 +8172,7 @@ impl<SP: Deref> FundedChannel<SP> where
81598172
}
81608173
}
81618174

8162-
let anchor_outputs_value_msat = if self.funding.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
8175+
let anchor_outputs_value_msat = if funding.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
81638176
ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000
81648177
} else {
81658178
0
@@ -8175,23 +8188,23 @@ impl<SP: Deref> FundedChannel<SP> where
81758188
}
81768189

81778190
let pending_value_to_self_msat =
8178-
self.funding.value_to_self_msat + htlc_stats.pending_inbound_htlcs_value_msat - removed_outbound_total_msat;
8191+
funding.value_to_self_msat + htlc_stats.pending_inbound_htlcs_value_msat - removed_outbound_total_msat;
81798192
let pending_remote_value_msat =
8180-
self.funding.get_value_satoshis() * 1000 - pending_value_to_self_msat;
8193+
funding.get_value_satoshis() * 1000 - pending_value_to_self_msat;
81818194

8182-
if !self.funding.is_outbound() {
8195+
if !funding.is_outbound() {
81838196
// `Some(())` is for the fee spike buffer we keep for the remote. This deviates from
81848197
// the spec because the fee spike buffer requirement doesn't exist on the receiver's
81858198
// side, only on the sender's. Note that with anchor outputs we are no longer as
81868199
// sensitive to fee spikes, so we need to account for them.
81878200
//
81888201
// A `None` `HTLCCandidate` is used as in this case because we're already accounting for
81898202
// the incoming HTLC as it has been fully committed by both sides.
8190-
let mut remote_fee_cost_incl_stuck_buffer_msat = self.context.next_remote_commit_tx_fee_msat(&self.funding, None, Some(()));
8191-
if !self.funding.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
8203+
let mut remote_fee_cost_incl_stuck_buffer_msat = self.context.next_remote_commit_tx_fee_msat(funding, None, Some(()));
8204+
if !funding.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
81928205
remote_fee_cost_incl_stuck_buffer_msat *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
81938206
}
8194-
if pending_remote_value_msat.saturating_sub(self.funding.holder_selected_channel_reserve_satoshis * 1000).saturating_sub(anchor_outputs_value_msat) < remote_fee_cost_incl_stuck_buffer_msat {
8207+
if pending_remote_value_msat.saturating_sub(funding.holder_selected_channel_reserve_satoshis * 1000).saturating_sub(anchor_outputs_value_msat) < remote_fee_cost_incl_stuck_buffer_msat {
81958208
log_info!(logger, "Attempting to fail HTLC due to fee spike buffer violation in channel {}. Rebalancing is required.", &self.context.channel_id());
81968209
return Err(LocalHTLCFailureReason::FeeSpikeBuffer);
81978210
}

0 commit comments

Comments
 (0)