Skip to content

Error if the calculated reserve would be greater than the channel value#4580

Open
tankyleo wants to merge 2 commits intolightningdevkit:mainfrom
tankyleo:2026-04-reserve-greater-than-channel-value
Open

Error if the calculated reserve would be greater than the channel value#4580
tankyleo wants to merge 2 commits intolightningdevkit:mainfrom
tankyleo:2026-04-reserve-greater-than-channel-value

Conversation

@tankyleo
Copy link
Copy Markdown
Contributor

Based on #4550, draft until parent goes in

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Apr 30, 2026

👋 Thanks for assigning @wpaulino 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.

@tankyleo tankyleo force-pushed the 2026-04-reserve-greater-than-channel-value branch from 7d64174 to 06a604d Compare April 30, 2026 01:52
@tankyleo tankyleo self-assigned this Apr 30, 2026
@tankyleo tankyleo moved this to Goal: Merge in Weekly Goals Apr 30, 2026
tankyleo added 2 commits May 6, 2026 18:02
In 0FC channels, capping the reserve to the total value of the channel
allowed a splice initiator to withdraw past their reserve in case the
acceptor had no balance in the channel.

This is because the post-splice value of the channel was equal to the
initiator's post splice balance. Hence, this post splice balance always
matched the reserve, even though the reserve was below the dust limit.

The only thing that prevented the initiator from withdrawing all their
balance was the script dust limit check in
`interactivetxs::NegotiationContext::receive_tx_add_output`.

In case the splice acceptor had any balance in the channel, or there
were HTLCs in the channel, or the channel was not 0FC, the
splice initiator's post-splice balance was always below the full channel
value. Hence when the reserve was capped at the channel value, the
post-splice balance was always below the reserve, and the splice was
rejected.

Also, in `validate_splice_contributions`, to determine the
`counterparty_selected_channel_reserve`, we now read the holder's dust
limit from the context, instead of the current global constant.
We made the same change to the calculation of the v2 reserve in the
previous commit.
@tankyleo tankyleo force-pushed the 2026-04-reserve-greater-than-channel-value branch from 06a604d to 9669465 Compare May 6, 2026 20:20
@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

❌ Patch coverage is 83.87097% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.12%. Comparing base (964a84f) to head (9669465).
⚠️ Report is 25 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/channel.rs 75.80% 15 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4580      +/-   ##
==========================================
- Coverage   86.18%   86.12%   -0.06%     
==========================================
  Files         156      157       +1     
  Lines      108528   108818     +290     
  Branches   108528   108818     +290     
==========================================
+ Hits        93532    93720     +188     
- Misses      12386    12485      +99     
- Partials     2610     2613       +3     
Flag Coverage Δ
tests 86.12% <83.87%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tankyleo tankyleo marked this pull request as ready for review May 7, 2026 05:30
@ldk-reviews-bot ldk-reviews-bot requested a review from joostjager May 7, 2026 05:30
@tankyleo tankyleo requested review from TheBlueMatt and wpaulino and removed request for joostjager May 7, 2026 05:31
== 0,
);
)
.map_err(|()| format!("The post-splice channel value {post_channel_value_sat} is smaller than our dust limit {MIN_CHAN_DUST_LIMIT_SATOSHIS}"))?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Bug: The error message references MIN_CHAN_DUST_LIMIT_SATOSHIS but the actual dust limit passed to get_v2_channel_reserve_satoshis on line 2822 is context.holder_dust_limit_satoshis. These can differ (e.g. in anchor channels with higher dust limits, or as demonstrated by the new test which sets holder_dust_limit_satoshis = 10_000). The error message will report an incorrect (lower) value when the holder's dust limit exceeds the minimum.

Suggested change
.map_err(|()| format!("The post-splice channel value {post_channel_value_sat} is smaller than our dust limit {MIN_CHAN_DUST_LIMIT_SATOSHIS}"))?;
.map_err(|()| format!("The post-splice channel value {post_channel_value_sat} is smaller than our dust limit {}", context.holder_dust_limit_satoshis))?;

@ldk-claude-review-bot
Copy link
Copy Markdown
Collaborator

Review Summary

Issues Found

  1. lightning/src/ln/channel.rs:2828 — Error message uses MIN_CHAN_DUST_LIMIT_SATOSHIS (354) but the actual dust limit passed to get_v2_channel_reserve_satoshis is context.holder_dust_limit_satoshis, which can be higher (e.g. in anchor channels). The error will report the wrong threshold value.

Notes on the rest of the diff

  • The removal of cmp::min(channel_value_satoshis, ...) in both get_holder_selected_channel_reserve_satoshis and get_v2_channel_reserve_satoshis is safe: the early Err checks ensure the inputs satisfy the invariants that previously required the cmp::min clamp. The documented guarantee ("no larger than channel_value_satoshis") still holds for Ok results.
  • The capping of their_channel_reserve_proportional_millionths at 1_000_000 in get_holder_selected_channel_reserve_satoshis matches the documented behavior in ChannelHandshakeConfig.
  • Moving the has_output check inside the zero-reserve branch of get_next_splice_out_maximum_sat is correct — non-zero reserve channels already ensure balance >= dust_limit via the reserve floor, and post_splice_delta_above_reserve_sat accounts for funder fees.
  • The is_0reserve short-circuit now happens after the dust/min-reserve check. This means 0-reserve channels with value below the dust limit are now rejected. This is a behavior change but correct — such channels would be useless regardless.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Goal: Merge

Development

Successfully merging this pull request may close these issues.

3 participants