Skip to content

Commit b1018d1

Browse files
committed
Check pending funding in send_update_fee
If there are any pending splices when an sending an update_fee message, the new fee rate must be validated against each pending FundingScope. Otherwise, it may be invalid once the splice is locked.
1 parent 1ac2f4c commit b1018d1

File tree

1 file changed

+42
-25
lines changed

1 file changed

+42
-25
lines changed

lightning/src/ln/channel.rs

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6738,31 +6738,11 @@ impl<SP: Deref> FundedChannel<SP> where
67386738
panic!("Cannot update fee while peer is disconnected/we're awaiting a monitor update (ChannelManager should have caught this)");
67396739
}
67406740

6741-
// Before proposing a feerate update, check that we can actually afford the new fee.
6742-
let dust_exposure_limiting_feerate = self.context.get_dust_exposure_limiting_feerate(&fee_estimator);
6743-
let htlc_stats = self.context.get_pending_htlc_stats(&self.funding, Some(feerate_per_kw), dust_exposure_limiting_feerate);
6744-
let commitment_data = self.context.build_commitment_transaction(
6745-
&self.funding, self.holder_commitment_point.transaction_number(),
6746-
&self.holder_commitment_point.current_point(), true, true, logger,
6747-
);
6748-
let buffer_fee_msat = commit_tx_fee_sat(feerate_per_kw, commitment_data.tx.nondust_htlcs().len() + htlc_stats.on_holder_tx_outbound_holding_cell_htlcs_count as usize + CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize, self.funding.get_channel_type()) * 1000;
6749-
let holder_balance_msat = commitment_data.stats.local_balance_before_fee_anchors_msat - htlc_stats.outbound_holding_cell_msat;
6750-
if holder_balance_msat < buffer_fee_msat + commitment_data.stats.total_anchors_sat * 1000 + self.funding.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 {
6751-
//TODO: auto-close after a number of failures?
6752-
log_debug!(logger, "Cannot afford to send new feerate at {}", feerate_per_kw);
6753-
return None;
6754-
}
6755-
6756-
// Note, we evaluate pending htlc "preemptive" trimmed-to-dust threshold at the proposed `feerate_per_kw`.
6757-
let max_dust_htlc_exposure_msat = self.context.get_max_dust_htlc_exposure_msat(dust_exposure_limiting_feerate);
6758-
if htlc_stats.on_holder_tx_dust_exposure_msat > max_dust_htlc_exposure_msat {
6759-
log_debug!(logger, "Cannot afford to send new feerate at {} without infringing max dust htlc exposure", feerate_per_kw);
6760-
return None;
6761-
}
6762-
if htlc_stats.on_counterparty_tx_dust_exposure_msat > max_dust_htlc_exposure_msat {
6763-
log_debug!(logger, "Cannot afford to send new feerate at {} without infringing max dust htlc exposure", feerate_per_kw);
6764-
return None;
6765-
}
6741+
core::iter::once(&self.funding)
6742+
.chain(self.pending_funding.iter())
6743+
.map(|funding| self.validate_send_update_fee(funding, feerate_per_kw, fee_estimator, logger))
6744+
.collect::<Result<(), ()>>()
6745+
.ok()?;
67666746

67676747
// Some of the checks of `can_generate_new_commitment` have already been done above, but
67686748
// it's much more brittle to not use it in favor of checking the remaining flags left, as it
@@ -6785,6 +6765,43 @@ impl<SP: Deref> FundedChannel<SP> where
67856765
})
67866766
}
67876767

6768+
fn validate_send_update_fee<F: Deref, L: Deref>(
6769+
&self, funding: &FundingScope, feerate_per_kw: u32,
6770+
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
6771+
) -> Result<(), ()>
6772+
where
6773+
F::Target: FeeEstimator,
6774+
L::Target: Logger,
6775+
{
6776+
// Before proposing a feerate update, check that we can actually afford the new fee.
6777+
let dust_exposure_limiting_feerate = self.context.get_dust_exposure_limiting_feerate(&fee_estimator);
6778+
let htlc_stats = self.context.get_pending_htlc_stats(funding, Some(feerate_per_kw), dust_exposure_limiting_feerate);
6779+
let commitment_data = self.context.build_commitment_transaction(
6780+
funding, self.holder_commitment_point.transaction_number(),
6781+
&self.holder_commitment_point.current_point(), true, true, logger,
6782+
);
6783+
let buffer_fee_msat = commit_tx_fee_sat(feerate_per_kw, commitment_data.tx.nondust_htlcs().len() + htlc_stats.on_holder_tx_outbound_holding_cell_htlcs_count as usize + CONCURRENT_INBOUND_HTLC_FEE_BUFFER as usize, funding.get_channel_type()) * 1000;
6784+
let holder_balance_msat = commitment_data.stats.local_balance_before_fee_anchors_msat - htlc_stats.outbound_holding_cell_msat;
6785+
if holder_balance_msat < buffer_fee_msat + commitment_data.stats.total_anchors_sat * 1000 + funding.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 {
6786+
//TODO: auto-close after a number of failures?
6787+
log_debug!(logger, "Cannot afford to send new feerate at {}", feerate_per_kw);
6788+
return Err(());
6789+
}
6790+
6791+
// Note, we evaluate pending htlc "preemptive" trimmed-to-dust threshold at the proposed `feerate_per_kw`.
6792+
let max_dust_htlc_exposure_msat = self.context.get_max_dust_htlc_exposure_msat(dust_exposure_limiting_feerate);
6793+
if htlc_stats.on_holder_tx_dust_exposure_msat > max_dust_htlc_exposure_msat {
6794+
log_debug!(logger, "Cannot afford to send new feerate at {} without infringing max dust htlc exposure", feerate_per_kw);
6795+
return Err(());
6796+
}
6797+
if htlc_stats.on_counterparty_tx_dust_exposure_msat > max_dust_htlc_exposure_msat {
6798+
log_debug!(logger, "Cannot afford to send new feerate at {} without infringing max dust htlc exposure", feerate_per_kw);
6799+
return Err(());
6800+
}
6801+
6802+
Ok(())
6803+
}
6804+
67886805
/// Removes any uncommitted inbound HTLCs and resets the state of uncommitted outbound HTLC
67896806
/// updates, to be used on peer disconnection. After this, update_*_htlc messages need to be
67906807
/// resent.

0 commit comments

Comments
 (0)