Skip to content

Conversation

wpaulino
Copy link
Contributor

Previously, we'd attempt to claim all HTLCs that have expired or that we have the preimage for on each preimage monitor update. This happened due to reusing the code path (get_counterparty_output_claim_info) used when producing all claims for a newly confirmed counterparty commitment. Unfortunately, this can result in invalid claim transactions and ultimately in loss of funds (if the HTLC expires and the counterparty claims it via the timeout), as it didn't consider that some of those HTLCs may have already been claimed by a separate transaction.

This commit changes the behavior when handling preimage monitor updates only. We will now only attempt to claim HTLCs for the specific preimage that we learned via the monitor update. This is safe to do, as even if a preimage HTLC claim transaction is reorged out, the OnchainTxHandler is responsible for continuous claiming attempts until we see a reorg of the corresponding commitment transaction.

We plan to rework this method in the following commit, so we start by
formatting it first.
Previously, we'd attempt to claim all HTLCs that have expired or that we
have the preimage for on each preimage monitor update. This happened due
to reusing the code path (`get_counterparty_output_claim_info`) used
when producing all claims for a newly confirmed counterparty commitment.
Unfortunately, this can result in invalid claim transactions and
ultimately in loss of funds (if the HTLC expires and the counterparty
claims it via the timeout), as it didn't consider that some of those
HTLCs may have already been claimed by a separate transaction.

This commit changes the behavior when handling preimage monitor updates
only. We will now only attempt to claim HTLCs for the specific preimage
that we learned via the monitor update. This is safe to do, as even if a
preimage HTLC claim transaction is reorged out, the `OnchainTxHandler`
is responsible for continuous claiming attempts until we see a reorg of
the corresponding commitment transaction.
@wpaulino wpaulino added this to the 0.2 milestone Oct 10, 2025
@wpaulino wpaulino requested a review from TheBlueMatt October 10, 2025 16:37
@wpaulino wpaulino self-assigned this Oct 10, 2025
@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Oct 10, 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.

Copy link

codecov bot commented Oct 10, 2025

Codecov Report

❌ Patch coverage is 93.24324% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.72%. Comparing base (faf7b8a) to head (46f0d31).
⚠️ Report is 16 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/chain/channelmonitor.rs 90.00% 8 Missing and 1 partial ⚠️
lightning/src/ln/monitor_tests.rs 98.27% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4154      +/-   ##
==========================================
+ Coverage   88.65%   88.72%   +0.06%     
==========================================
  Files         180      180              
  Lines      135241   135732     +491     
  Branches   135241   135732     +491     
==========================================
+ Hits       119895   120425     +530     
+ Misses      12579    12538      -41     
- Partials     2767     2769       +2     
Flag Coverage Δ
fuzzing 21.71% <45.55%> (-0.05%) ⬇️
tests 88.56% <93.24%> (+0.07%) ⬆️

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.

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.

Thanks. Pretty straightforward fix, even if it ends with a bit of code duplication. Gonna go ahead and land this so we can backport it to 0.1 but whoever the bot picks as a second reviewer can also take a look afterwards.

@ldk-reviews-bot
Copy link

👋 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.

@ldk-reviews-bot
Copy link

✅ Added second reviewer: @tankyleo

@TheBlueMatt TheBlueMatt merged commit 652f0f1 into lightningdevkit:main Oct 10, 2025
24 checks passed
@TheBlueMatt TheBlueMatt mentioned this pull request Oct 10, 2025
@wpaulino wpaulino deleted the ladder-preimage-claim-fix branch October 10, 2025 21:38
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.

ACK, two possible follow-ups I have in mind.

let mut txn = nodes[1].tx_broadcaster.txn_broadcast();
assert_eq!(txn.len(), 1, "{:?}", txn.iter().map(|tx| tx.compute_txid()).collect::<Vec<_>>());
let htlc_claim_tx = txn.remove(0);
assert_eq!(htlc_claim_tx.input.len(), 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

Add coverage for the case where at this point we should claim multiple outputs on the commit tx ? I'm thinking of the MPP case. The actual code does this correctly.

fn get_counterparty_output_claims_for_preimage(
&self, preimage: PaymentPreimage, funding_spent: &FundingScope, commitment_number: u64,
commitment_txid: Txid,
per_commitment_option: Option<&Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Wondering if we can be more aggressive and remove the Option here - we have this data at all places where this is called. If not, we lose funds, and we should panic ? Also applies to get_point_for_commitment_number.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants