Skip to content

Update fee and dust handling for zero fee channels #3884

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 17, 2025

Conversation

carlaKC
Copy link
Contributor

@carlaKC carlaKC commented Jun 23, 2025

This PR completes the off-chain handling of V3 channels, as described in #3789.

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Jun 23, 2025

👋 Thanks for assigning @TheBlueMatt as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@carlaKC
Copy link
Contributor Author

carlaKC commented Jun 23, 2025

This still needs a few tests, opening up early for conceptual review.

@@ -5078,6 +5092,7 @@ where
) -> u64 {
if funding.get_channel_type().supports_anchor_zero_fee_commitments() {
debug_assert_eq!(self.feerate_per_kw, 0);
debug_assert!(fee_spike_buffer_htlc.is_none());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering whether it's worth having this assertion (which makes it necessary to check channel type before calling fee functions). The alternative would be to just ignore the parameters completely for zero fee channels (even though Some fee_spike_buffer_htlc doesn't make sense for the type).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't say I have a strong preference. The assertion seems fine to me.

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All LGTM, didn't carefully check every hunk, though.

@@ -133,6 +134,22 @@ enum FeeUpdateState {
Outbound,
}

/// Returns the fees for success and timeout second stage HTLC transactions.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Probably belongs in chan_utils.rs given it kinda mirrors commit_and_htlc_tx_fees_sat and is also called from there?

@@ -5078,6 +5092,7 @@ where
) -> u64 {
if funding.get_channel_type().supports_anchor_zero_fee_commitments() {
debug_assert_eq!(self.feerate_per_kw, 0);
debug_assert!(fee_spike_buffer_htlc.is_none());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't say I have a strong preference. The assertion seems fine to me.

@carlaKC carlaKC force-pushed the 3789-fees-and-dust branch from 001c8e9 to 4157d24 Compare July 10, 2025 13:16
@carlaKC carlaKC marked this pull request as ready for review July 10, 2025 13:32
@carlaKC carlaKC removed the request for review from joostjager July 10, 2025 13:32
@carlaKC carlaKC force-pushed the 3789-fees-and-dust branch from 4157d24 to 5fe6fa7 Compare July 10, 2025 13:40
@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Contributor

@elnosh elnosh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still grokking some of the changes here and the zero-fee commitment proposal but from my kind of limited context it LGTM (:

@carlaKC carlaKC force-pushed the 3789-fees-and-dust branch from 5fe6fa7 to f76a270 Compare July 15, 2025 19:33
@carlaKC
Copy link
Contributor Author

carlaKC commented Jul 15, 2025

Addressed review: only major change is moving responsibility for not triggering update_fee on zero fee channels to channelmanger (as there's been no change between new/old fee) and changing channel to just assert that zero fee channels don't hit update code.

Copy link
Contributor

@tankyleo tankyleo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM mod two nits, feel free to squash / ping second reviewer :)

carlaKC and others added 7 commits July 16, 2025 14:05
This fee rate is currently used in two scenarios:
- To count any fees above what we consider to be a sane estimate towards
  our dust exposure.
- To get a maximum dust exposure (when using
  MaxDustHTLCExposure::FeeEstimator strategy).

When we have zero fee commitments:
- Commitments are zero fee, so we don't need to count fees towards dust
  exposure.
- The amount of dust we have is not dependent on fees, as everything is
  zero fee.
- We still want to limit our total dust exposure.

This commit updates get_dust_exposure_limiting_feerate to allow a None
value to prepare for support for zero fee commitments. This clearly
allows us to indicate when we don't care about fee rates for dust
considerations.

In get_max_dust_htlc_exposure_msat, we simply hardcode a value of
1 sat/vbyte if a feerate dependent strategy is being used.
LDK does not support a channel type that supports both zero fee and
nonzero fee anchors (as this is impossible), so we can drop the
unnecessary check in build_htlc_output.
This commit pulls calculation of second stage fees into a helper
function. A side effect of this refactor is that it fixes a rounding
issue in commit_and_htlc_tx_fees_sat. Previously, rounding down of
fees would happen after multiplying by the number of HTLCs. Now the
fees will be rounded down before multiplying by the number of HTLCs.

This wasn't a serious issue - it would just cause us very slightly
over estimate our dust exposure at certain fee amounts that needed
rounding. A hard-coded value in test_nondust_htlc_excess_fees_are_dust
is updated to account for this rounding change.
Update second_stage_tx_fees_sat to return zero for zero fee commitments.
As is the case for anchors_zero_fee_commitments, this changes ensures
that we won't trim the second stage transaction fee off of the HTLC
amount.
When we have zero fee commitments, we don't need to calculate our fee
rate or check that it isn't stale because it is always zero.

Co-authored-by: Matt Corallo <[email protected]>
@carlaKC carlaKC force-pushed the 3789-fees-and-dust branch 2 times, most recently from b421d59 to 60064d1 Compare July 16, 2025 18:13
@carlaKC
Copy link
Contributor Author

carlaKC commented Jul 16, 2025

Squashed + addressed last 2x comments + rebased.

Edit: plus small silent rebase failure, new test on master needed an import this branch deleted.

carlaKC added 2 commits July 16, 2025 14:28
Update test helper in preparation to test zero fee commitments, where
the local balance will differ from the previously hardcoded value.
@carlaKC carlaKC force-pushed the 3789-fees-and-dust branch from 60064d1 to 54559b3 Compare July 16, 2025 18:35
@tankyleo tankyleo requested a review from TheBlueMatt July 16, 2025 19:04
};
let real_dust_limit_success_sat = htlc_success_dust_limit + context.holder_dust_limit_satoshis;
let real_dust_limit_timeout_sat = htlc_timeout_dust_limit + context.holder_dust_limit_satoshis;
if funding.get_channel_type().supports_anchor_zero_fee_commitments() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking - should the comment on this method be updated? the htlc is not an Option

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One nit and one thing maybe worth following up on, but not worth holding up on.

}

/// Returns a fee estimate for the commitment transaction depending on channel type.
pub(super) fn commitment_sat_per_1000_weight_for_type<F: Deref>(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a huge fan of the name because it hides which fee types we're using - we're using the "feerates that we'd want to assign to a channel" types, which are different from the "minimum/maximum we'd let our peer assign to a channel", but the name doesn't capture that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you think of anything less atrociously long than holder_preferred_commitment_sat_per_1000_weight_for_type?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

selected_commitment_sat_per_1000_weight?

} else {
non_anchor_feerate
};
let new_feerate = commitment_sat_per_1000_weight_for_type(&self.fee_estimator, funded_chan.funding.get_channel_type());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, would be nice to keep the old behavior where we fetch the feerates outside the loop. In general implementations of the FeeEstimator trait should be pretty effecient, but if we can reduce the number of calls we probably should. Same above in maybe_update_chan_fees.

@TheBlueMatt TheBlueMatt merged commit eae2bb1 into lightningdevkit:main Jul 17, 2025
25 of 26 checks passed
@github-project-automation github-project-automation bot moved this from Goal: Open to Done in Weekly Goals Jul 17, 2025
@carlaKC carlaKC mentioned this pull request Jul 17, 2025
TheBlueMatt added a commit that referenced this pull request Jul 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

5 participants