Include any pending fee updates in `can_accept_incoming_htlc` stats
What changed, and why it matters
This change makes a Lightning channel more conservative when deciding whether to accept an incoming payment for forwarding. It now considers fee increases that have been proposed but not yet acknowledged by the other party. This reduces the risk that the node accepts a payment it cannot actually afford to forward once the higher fees take effect, which could lead to a forced channel closure or loss of funds.
Review related fee-update and HTLC-acceptance paths for similar stale-feerate assumptions; consider regression tests covering pending fee updates during incoming HTLC evaluation; monitor for related disclosures from the Lightning Dev Kit team.
Security signals we found
Fee-update handling in HTLC acceptance logic
Conservative reserve/fee constraint checking
Potential channel force-close risk from stale feerate assumptions
No explicit CVE or security advisory referenced
Evidence from the diff
The patch modifies can_accept_incoming_htlc in lightning/src/ln/channel.rs to use the maximum of the current feerate (self.feerate_per_kw) and any pending fee update (self.pending_update_fee) when computing commitment statistics for both the next local and next remote commitment. Previously only the current feerate was used. Because pending fee updates may soon be reflected in commitments, ignoring them could cause the node to accept HTLCs that would violate reserve or fee constraints under the updated feerate, potentially leading to commitment failures or channel force-closes.
Changed components
lightning/src/ln/channel.rscan_accept_incoming_htlccommitment statistics calculationInspect captured patch +5 / −2
diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index 61e402e..4cb417f 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -5031,6 +5031,9 @@ where
// single HTLC here, not the entire channel, so we opt to be conservative
// in what we accept to forward.
let include_counterparty_unknown_htlcs = true;
+ // Similar reasoning as above
+ let feerate =
+ cmp::max(self.feerate_per_kw, self.pending_update_fee.map(|(fee, _)| fee).unwrap_or(0));
// A `None` `HTLCCandidate` is used as in this case because we're already accounting for
// the incoming HTLC as it has been fully committed by both sides.
let next_local_commitment_stats = self
@@ -5039,7 +5042,7 @@ where
None,
include_counterparty_unknown_htlcs,
fee_spike_buffer_htlc,
- self.feerate_per_kw,
+ feerate,
dust_exposure_limiting_feerate,
)
.map_err(|()| {
@@ -5052,7 +5055,7 @@ where
None,
include_counterparty_unknown_htlcs,
fee_spike_buffer_htlc,
- self.feerate_per_kw,
+ feerate,
dust_exposure_limiting_feerate,
)
.map_err(|()| {
Why this scored 46/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.