Uh oh!
There was an error while loading. Please reload this page.
Validate funding contributions reserves in splice_init and splice_ack handling - #4011
Conversation
👋 Thanks for assigning @wpaulino as a reviewer! |
splice_ack and splice_init messagessplice_init and splice_ack messages6d6b07c to
93965c6CompareCodecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@## main #4011 +/- ##
==========================================
+ Coverage 88.76% 88.77% +0.01%
==========================================
Files 176 176 Lines 129345 129357 +12 Branches 129345 129357 +12 ==========================================
+ Hits 114812 114836 +24 + Misses 11925 11921 -4 + Partials 2608 2600 -8
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
ldk-reviews-bot
commented
Aug 16, 2025
🔔 1st Reminder Hey @wpaulino! This PR has been waiting for your review. |
ldk-reviews-bot
commented
Aug 18, 2025
🔔 2nd Reminder Hey @wpaulino! This PR has been waiting for your review. |
tankyleo
commented
Aug 19, 2025
On hold until splice-out PR gets in. |
ldk-reviews-bot
commented
Aug 20, 2025
🔔 3rd Reminder Hey @wpaulino! This PR has been waiting for your review. |
93965c6 to
d0023e3CompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ldk-reviews-bot
commented
Aug 26, 2025
👋 The first review has been submitted! Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer. |
d0023e3 to
835b67bCompareRebase on merge-base (diff):
|
splice_init and splice_ack messagessplice_init and splice_ack handling835b67b to
453a211Comparetankyleo
commented
Aug 27, 2025
Amend: (diff)
|
Uh oh!
There was an error while loading. Please reload this page.
tankyleo
commented
Aug 27, 2025
Rebasing now to fix conflict... |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
8b3004e to
ce4161cCompareUh oh!
There was an error while loading. Please reload this page.
| let their_channel_balance = Amount::from_sat(self.funding.get_value_satoshis()) | ||
| - Amount::from_sat(self.funding.get_value_to_self_msat() / 1000); | ||
| let post_channel_balance = AddSigned::checked_add_signed( | ||
| let adjusted_funding_contribution = if let Some((contribution, is_initiator, feerate)) = |
There was a problem hiding this comment.
Are we sure this is right in the splice_ack case? In the splice_channel (initiate outbound splice) case, we pass the total contribution ignoring fees, which then mandates that we subtract the fee here. But then we go to test again in splice_ack and, AFAICT we pass the adjusted contribution at that point (which is what we send to our counterparty, or the their_funding_contribution used below), and then we shouldn't subtract the fee here. IMO we should pull this back out to the splice_channel method and keep this method strictly a symmetrical splice validation function that doesn't have any sender/recipient-specific logic at all. Once we make get_next_{local,remote}_commitment_stats fallible it'll just be a matter of calling that and checking the reserve, basically.
There was a problem hiding this comment.
Will look into it thank you
But then we go to test again in splice_ack and, AFAICT we pass the adjusted contribution at that point (which is what we send to our counterparty, or the their_funding_contribution used below), and then we shouldn't subtract the fee here.
When we receive splice_ack, we set this triple to None, and we don't subtract the fee here ?
I'll revisit this part in any case.
There was a problem hiding this comment.
Ah, sorry, duh, all the more confusing code tho :)
1ce710f to
b6757a5Compare
TheBlueMatt
left a comment
There was a problem hiding this comment.
Okay, thanks for rewriting this four times 😅. Feel free to squash (looks like the latest fixup needs to get moved back a bit), I think this LGTM. cc @wpaulino
TheBlueMatt
commented
Sep 2, 2025
Also sadly needs rebase. |
b6757a5 to
c96dba4Comparetankyleo
commented
Sep 2, 2025
First rebase: no diff, squash fixups, reorganize commits. |
As much as possible, we want to only mutate state once we are done with input validation. This also removes complaints when helper functions during validation take a `&self`.
As in `splice_init`, this helps clearly delineate `splice_ack` message validation from the subsequent state mutations. This is a code-move.
`NextCommitmentStats` provides the commitment transaction fee as a separate value to assist with applying a multiplier on it in `can_accept_incoming_htlc`. Nonetheless in most cases, we want the balances to include the commitment transaction fee, so here we add a helper that gives us these balances. Also make the style of `tx_builder::subtract_addl_outputs` consistent with `get_balances_including_fee`.
The reserve we should maintain on our own transaction should be greater than our own dust limit.
We check this when validating `splice_init`, `splice_ack` messages, and also when validating user-specified contributions. From BOLT 2: ``` - If `funding_contribution_satoshis` is negative and its absolute value is greater than the sending node's current channel balance: - MUST send a `warning` and close the connection or send an `error` and fail the channel. ``` and further down: ``` If a side does not meet the reserve requirements, that's OK: but if they take funds out of the channel, they must ensure that they do meet them. If your peer adds a massive amount to the channel, then you only have to add more reserve if you want to contribute to the splice (and you can use `tx_remove_output` and/or `tx_remove_input` part-way through if this happens). ``` Therefore, we check the v2 reserve anytime `funding_contribution_satoshis` is not equal to zero. We allow parties to draw from their previous reserve, as long as they satisfy their v2 reserve.
c96dba4 to
85a02caCompareUh oh!
There was an error while loading. Please reload this page.
TheBlueMatt
left a comment
There was a problem hiding this comment.
One question, otherwise LGTM
| // New reserve values are based on the new channel value and are v2-specific | ||
| let counterparty_selected_channel_reserve_satoshis = Some(get_v2_channel_reserve_satoshis( | ||
| let counterparty_selected_channel_reserve_satoshis = | ||
| Some(get_v2_channel_reserve_satoshis(post_channel_value, MIN_CHAN_DUST_LIMIT_SATOSHIS)); |
There was a problem hiding this comment.
Wait, I thin this is wrong (and also maybe reserves should be higher than both dust limits?). Not sure what the spec says, but the point of the reserve is to have something that we can be punished for when broadcasting our own stale transaction. Thus, the reserve we keep should really be higher than our own dust limit (which applies to our local commitment transactions that we can broadcast) so that if we broadcast a stale state we can be punished.
There was a problem hiding this comment.
Isn't that what we have here ?
MIN_CHAN_DUST_LIMIT_SATOSHIS: our own dust limit
counterparty_selected_channel_reserve_satoshis: we must keep at least these many sats "punishable" on our broadcastable transaction.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.