Skip to content

Prefactor for Trampoline MPP accumulation#4510

Open
carlaKC wants to merge 18 commits intolightningdevkit:mainfrom
carlaKC:2299-mpp-prefactor
Open

Prefactor for Trampoline MPP accumulation#4510
carlaKC wants to merge 18 commits intolightningdevkit:mainfrom
carlaKC:2299-mpp-prefactor

Conversation

@carlaKC
Copy link
Copy Markdown
Contributor

@carlaKC carlaKC commented Mar 24, 2026

This PR contains a set of refactors pulled out of #4493:

  • Extract common timeout / MPP logic into separate functions
  • Allow construction of blinded trampoline paths
  • Misc constructors + validation

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented Mar 24, 2026

👋 Thanks for assigning @valentinewallace 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 carlaKC force-pushed the 2299-mpp-prefactor branch from 40f75a4 to f15271e Compare March 24, 2026 17:19
@ldk-claude-review-bot
Copy link
Copy Markdown
Collaborator

ldk-claude-review-bot commented Mar 24, 2026

No issues found.

This is a re-review of the same PR. My prior review covered all the critical invariants and found no issues. After a second thorough pass examining the entire diff, I confirm:

  • Serialization compatibility: All TLV tags unchanged. write_claimable_htlc/Readable correctly route through mpp_part with identical tags. timer_ticks correctly reset to 0 on deserialization.
  • check_incoming_mpp_part / handle_claimable_htlc: The first_claimable_htlc guard correctly prevents failing HTLCs committed to new entries. All error paths in check_incoming_mpp_part return before pushing the HTLC.
  • fail_htlc! macro: htlc_source and value are correctly constructed before the macro definition and before claimable_htlc is moved. The continue 'next_forwardable_htlc ensures htlc_source is moved at most once.
  • MPP completion condition: Matches exactly between check_incoming_mpp_part (total_intended_recvd_value >= total_mpp_value) and check_mpp_timeout (same condition).
  • claim_funds_from_hop signature: Changed to &HTLCPreviousHopData — only reads fields, no ownership needed. All call sites pass references.
  • check_incoming_htlc_cltv parameterization: Both call sites pass MIN_CLTV_EXPIRY_DELTA, matching the previously hardcoded value.
  • ForwardTlvsInfo trait / ForwardNode<F> genericization: Correctly propagated with ForwardTlvs as the default where needed. Sealed trait prevents external implementation.
  • TrampolineForward field rename: incoming_shared_secrettrampoline_shared_secret at TLV tag 0 — name-only change, serialization compatible.
  • previous_hop_data() helper: Correctly returns slice of previous hop data for all HTLCSource variants.
  • check_mpp_timeout timer_ticks behavior: Minor difference from old code (ticks now incremented for completed MPPs before early-return), but functionally benign since the function returns false for complete MPPs regardless.
  • Test helper create_trampoline_forward_blinded_tail: Correctly computes fee_msat from BlindedPaymentPath::payinfo rather than hardcoding final_value_msat — this is more correct for single-hop blinded paths where the fee should be 0.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 24, 2026

Codecov Report

❌ Patch coverage is 91.08527% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.19%. Comparing base (ab31f99) to head (f15271e).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/channelmanager.rs 94.36% 12 Missing ⚠️
lightning/src/blinded_path/payment.rs 75.60% 9 Missing and 1 partial ⚠️
lightning/src/ln/onion_payment.rs 75.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main    #4510    +/-   ##
========================================
  Coverage   86.19%   86.19%            
========================================
  Files         160      160            
  Lines      107537   107713   +176     
  Branches   107537   107713   +176     
========================================
+ Hits        92693    92848   +155     
- Misses      12220    12238    +18     
- Partials     2624     2627     +3     
Flag Coverage Δ
tests 86.19% <91.08%> (+<0.01%) ⬆️

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.

@valentinewallace valentinewallace requested review from valentinewallace and removed request for wpaulino March 25, 2026 15:57
@ldk-reviews-bot
Copy link
Copy Markdown

🔔 1st Reminder

Hey @valentinewallace! 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.

@ldk-reviews-bot
Copy link
Copy Markdown

🔔 2nd Reminder

Hey @valentinewallace! 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.

@carlaKC
Copy link
Copy Markdown
Contributor Author

carlaKC commented Mar 30, 2026

Some discussion in the dependent PR, taking @valentinewallace off as a reviewer for now to save some bot-spam.

@carlaKC carlaKC removed the request for review from valentinewallace March 30, 2026 12:30
@carlaKC carlaKC force-pushed the 2299-mpp-prefactor branch from f15271e to 6aae0dd Compare April 1, 2026 21:24
@carlaKC
Copy link
Copy Markdown
Contributor Author

carlaKC commented Apr 2, 2026

Updated to include suggested refactor, and pulled some of the blinded path test utils up into this PR as well.

@carlaKC carlaKC force-pushed the 2299-mpp-prefactor branch from 6aae0dd to 0e82461 Compare April 2, 2026 14:51
@carlaKC carlaKC requested a review from valentinewallace April 6, 2026 14:21
Copy link
Copy Markdown
Contributor

@valentinewallace valentinewallace left a comment

Choose a reason for hiding this comment

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

Nothing blocking, thanks for that mpp part refactor! Probably can get a 2nd reviewer on here. I didn't review the code moves that carefully but claude said they're legit


impl ClaimableHTLC {
// Increments timer ticks and returns a boolean indicating whether HTLC is timed out.
fn mpp_timer_tick(&mut self) -> bool {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: I'd prefer to in-line this if it's only used in one place

user_channel_id: val.prev_hop.user_channel_id.unwrap_or(0),
cltv_expiry: val.cltv_expiry,
value_msat: val.value,
counterparty_node_id: val.mpp_part.prev_hop.counterparty_node_id,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: it looks like mpp_part.prev_hop is accessed in a million places - might be nice to have a helper

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Considered this but I found it a bit ick to have some fields accessed with a helper and others directly - direct access also didn't mess up the formatting quite enough for it to be worthwhile.

Defer to second reviewer to make a call? Happy to change !

};
check_total_value!(purpose);

if let Err(_) = self.handle_claimable_htlc(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: Err(()) if possible in case it changes so we're forced to update it

@ldk-reviews-bot
Copy link
Copy Markdown

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

@carlaKC
Copy link
Copy Markdown
Contributor Author

carlaKC commented Apr 7, 2026

Addressed 2x nits.

@ldk-reviews-bot
Copy link
Copy Markdown

✅ Added second reviewer: @joostjager

@joostjager joostjager removed their request for review April 8, 2026 07:06
@carlaKC carlaKC requested a review from TheBlueMatt April 8, 2026 20:32
Copy link
Copy Markdown
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.

Only one real comment.

sender_intended_value: outgoing_amt_msat,
timer_ticks: 0,
total_value_received: None,
htlc_value,
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.

I will say, constructors in rust are way less readable then destructuring where you get the parameter name beside the value we're assigning :/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah fair, I was also on the fence - will remove


/// Returns a boolean indicating whether the HTLC has timed out on chain, accounting for a buffer
/// that gives us time to resolve it.
fn check_onchain_timeout(&self, height: u32, buffer: u32) -> bool {
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.

nit: should buffer be hard-coded? its currently a constant across the code base and while we might want to make that configurable it probably should be constant between trampoline and not-trampoline.

let ref mut claimable_payment = claimable_payments
.claimable_payments
.entry(payment_hash)
// Note that if we insert here we MUST NOT fail_htlc!()
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.

We don't fail_htlc from here anymore.

/// Both [`ForwardTlvs`] (channel-based forwarding) and [`TrampolineForwardTlvs`] (trampoline
/// node-based forwarding) implement this trait, allowing blinded path construction to be generic
/// over the forwarding mechanism.
pub trait ForwardTlvsInfo: Writeable + Clone {
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.

hmm, do we really want people implementing their own ForwardTlvsInfo? Should we instead make it an internal thing and expose wrapper types?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Going with a sealed supertrait b/c it felt a bit cleaner and we have the pattern elsewhere.
Happy to do a wrapper if that's your pref!

carlaKC added 3 commits April 10, 2026 14:11
Pull out all fields that are common to incoming claimable and trampoline
MPP HTLCs. This will be used in future commits to accumulate MPP HTLCs
that are part of trampoline forwards - we can't claim these, but need
to accumulate them in the same way as receives before forwarding onwards.
carlaKC added 15 commits April 10, 2026 14:11
We'll use this shared logic when we need to timeout trampoline HTLCs.

Note that there's a slight behavior change in this commit. Previously,
we'd do a first pass to check out total received value and return
early if we'd reached it without applying a MPP tick to any HTLC.
Now, we'll apply the MPP tick as we accumulate our total value received.

This does not make any difference, because we never MPP-timeout fully
accumulated MPP payments so it doesn't matter if we've applied the
tick when we've reached our full amount.
We'll re-use this to check trampoline MPP timeout in future commits.
In the commit that follows we're going to need to take ownership
of our htlc before this macro is used, so we pull out the information
we need in advance.
We're going to use the same logic for trampoline and for incoming MPP
payments, so we pull this out into a separate function.
To allow re-use with trampoline payments which won't use the
ClaimablePayment type, make handling generic for anything with MPP
parts.

Here we also move counterparty skimmed logic to claimable payments,
as this doesn't apply for trampoline.
For trampoline payments, we don't want to enforce a minimum cltv delta
between our incoming and outer onion outgoing CLTV because we'll
calculate our delta from the inner trampoline onion's value. However,
we still want to check that we get at least the CLTV that the sending
node intended for us and we still want to validate our incoming value.
Refactor to allow setting a zero delta, for use for trampoline payments.
To use helper functions for either trampoline or regular paths.
We only want to support trampoline and regular blinded paths, so we
don't want to support implementation outside of this crate. We need
the trait itself to be public as it's part of a public struct.
To create trampoline forwarding and single hop receiving tails.
@carlaKC carlaKC force-pushed the 2299-mpp-prefactor branch from b2ac8f4 to 380f744 Compare April 10, 2026 18:45
@carlaKC
Copy link
Copy Markdown
Contributor Author

carlaKC commented Apr 10, 2026

Diff for last review round!

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

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

5 participants