Skip to content

Trampoline Forwarding: Enforce trampoline constraints #3983

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

a-mpch
Copy link
Contributor

@a-mpch a-mpch commented Aug 2, 2025

This PR is part of 1/N PRs in order to split up #3976

This commit checks amount and CLTV of the trampoline to the outer hop. If exceeds limitations we fail the forward using a InboundHTLCErr with specified local reason.

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Aug 2, 2025

👋 Thanks for assigning @tankyleo 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 Aug 2, 2025

Codecov Report

❌ Patch coverage is 84.00000% with 48 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.83%. Comparing base (381416a) to head (5c6f023).
⚠️ Report is 47 commits behind head on main.

Files with missing lines Patch % Lines
lightning/src/ln/onion_payment.rs 48.38% 31 Missing and 1 partial ⚠️
lightning/src/ln/blinded_payment_tests.rs 95.67% 7 Missing and 3 partials ⚠️
lightning/src/ln/onion_utils.rs 0.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3983      +/-   ##
==========================================
- Coverage   88.94%   88.83%   -0.11%     
==========================================
  Files         174      175       +1     
  Lines      124575   127997    +3422     
  Branches   124575   127997    +3422     
==========================================
+ Hits       110797   113706    +2909     
- Misses      11278    11721     +443     
- Partials     2500     2570      +70     
Flag Coverage Δ
fuzzing 21.84% <0.00%> (-0.35%) ⬇️
tests 88.66% <84.00%> (-0.10%) ⬇️

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.

@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

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

This is a more mechanical review as I am not familiar with this part of the codebase :)

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

@tankyleo
Copy link
Contributor

tankyleo commented Aug 6, 2025

Also when I remove everything but the test code, the test does not fail - is that intentional ? I have not reviewed the test

@a-mpch
Copy link
Contributor Author

a-mpch commented Aug 6, 2025

thanks @tankyleo !

Also when I remove everything but the test code, the test does not fail - is that intentional ? I have not reviewed the test

Double checked the tests and yes, this happens because when receiving a trampoline it behaves exactly as receiving a non-trampoline payment. Having the same failure scenario.
I figured out that for this PR it's ok to have one test that checks for the CLTV constraint and later when having the capacity of forwarding we can test the constraint when forwarding related to the amt.

I added fixups for each of your comments and rebased in top of main

@a-mpch a-mpch force-pushed the 2025-08-enforce-trampoline-constraint branch from 4deaa3e to d37d6d8 Compare August 6, 2025 20:35
}
{
let payment_failed_conditions = PaymentFailedConditions::new()
.expected_htlc_error_data(LocalHTLCFailureReason::FinalIncorrectHTLCAmount, &[0, 0, 0, 0, 0, 0, 3, 232]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar question here about where the 3, 232 comes from :)

Copy link
Contributor

Choose a reason for hiding this comment

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

@a-mpch Let's also do this here ? let expected_error_data = amt_msat.to_be_bytes();

@tankyleo
Copy link
Contributor

tankyleo commented Aug 7, 2025

Also go ahead and squash next time you push, and expand the commit message to describe in detail what you are doing, including in the tests :) Would help me out with reviewing the code too thank you !

@a-mpch a-mpch force-pushed the 2025-08-enforce-trampoline-constraint branch from d37d6d8 to 3515844 Compare August 7, 2025 22:01
@a-mpch
Copy link
Contributor Author

a-mpch commented Aug 7, 2025

@tankyleo Reading the initial PR needed a lot of explanation work 😅, thanks for the detailed review!

All your comments should have been addressed and rebased and squashed.

note that linting CI seems to be a problem on one of the new commits after rebase

@a-mpch a-mpch requested a review from tankyleo August 11, 2025 15:18
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.

Another round of review thank you !

Also proofread the commit message, there are a few mistakes in there :)

We add a `check_trampoline_constraints` similar to
`check_blinded_path_constraints` that compares the Trampoline onion's amount
and CLTV values to the limitations imposed by the outer onion.

We also add the expected errors by the spec: `TemporaryTrampolineFailure`,
`TrampolineFeeOrExpiryInsufficient` and `UnknownNextTrampoline` to be used
when encountering errors during forwarding or constraint validation.

Finally, we add and modify the following tests:
- Modified the unblinded receive to validate when receiving amount less than
expected
- Modified test with wrong CLTV parameters that now fails with new enforcement
of CLTV limits
- Add unblinded receive test that forces trampoline onion's CLTV to be greater
than the outer onion packet

Note that there are some TODOs to be fixed in following commits as we need
the full trampoline forwarding feature to effectively test all cases.

Co-authored-by: Arik Sosman <[email protected]>
@a-mpch a-mpch force-pushed the 2025-08-enforce-trampoline-constraint branch from 3515844 to 5c6f023 Compare August 13, 2025 19:49
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.

Just two last questions, then a couple of drive-by nits again thank you for all the work.

@@ -306,6 +349,15 @@ pub(super) fn create_recv_pending_htlc_info(
}
})?;
let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat };
check_trampoline_onion_constraints(outer_hop_data, cltv_expiry_height, sender_intended_htlc_amt_msat).map_err(|e| {
Copy link
Contributor

Choose a reason for hiding this comment

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

I understand that we don't have test coverage for the forwarding code paths, but here we don't have test coverage - should we be adding a test for blinded trampoline receives ?

Comment on lines +1681 to +1687
/// We have been unable to forward a payment to the next Trampoline node but may be able to
/// do it later.
TemporaryTrampolineFailure,
/// The amount or CLTV expiry were insufficient to route the payment to the next Trampoline.
TrampolineFeeOrExpiryInsufficient,
/// The specified next Trampoline node cannot be reached from our node.
UnknownNextTrampoline,
Copy link
Contributor

Choose a reason for hiding this comment

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

I would have expected these to be used in this PR since the commit message says

We also add the expected errors by the spec: `TemporaryTrampolineFailure`,
`TrampolineFeeOrExpiryInsufficient` and `UnknownNextTrampoline` to be used
when encountering errors during forwarding or constraint validation.

But as far as I see they are not used anywhere yet, can you help me understand ? thank you again !

// Creates custom onion packet where the final trampoline hop uses unblinded receive format
// (not natively supported) to validate payment amount verification.
// - When underpay=false: Payment succeeds with correct amount
// - When underpay=true: Payment fails due to amount mismatch (sends 2x expected amount)
Copy link
Contributor

Choose a reason for hiding this comment

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

The blurb is great thank you ! If we send 2x the expected amount, shouldn't the boolean variable name be overpay ?

TrampolineHop {
pubkey: carol_node_id,
node_features: Features::empty(),
fee_msat: 0, // no trampoline fee becuase we are receiving.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: fix typo


// Get the original inner session private key that the ChannelManager generated so we can
// re-use it for the outer session private key. This way HMAC validation in attributable
// errors do not makes the test fail.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: does not make the test fail.

},
],
hops: carol_blinded_hops,
blinding_point: carol_blinding_point,
// This will be ignored becase we force the cltv_expiry of the trampoline hop.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: because

let outer_session_priv = secret_from_hex("e52c20461ed7acd46c4e7b591a37610519179482887bd73bf3b94617f8f03677");
// Get the original inner session private key that the ChannelManager generated so we can
// re-use it for the outer session private key. This way HMAC validation in attributable
// errors do not makes the test fail.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: does not make the test fail.

let err = InboundHTLCErr {
reason: LocalHTLCFailureReason::FinalIncorrectCLTVExpiry,
err_data: outer_hop_data.outgoing_cltv_value.to_be_bytes().to_vec(),
msg: "Trampoline onion's CLTV values exceeded the outer onion's",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: use singular value

let err = InboundHTLCErr {
reason: LocalHTLCFailureReason::FinalIncorrectHTLCAmount,
err_data: outgoing_amount.to_be_bytes().to_vec(),
msg: "Trampoline onion's amt values exceeded the outer onion's",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: same here let's use singular value

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.

3 participants