Skip to content
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

Intercept HTLC forwards for JIT channels #1835

Conversation

valentinewallace
Copy link
Contributor

@valentinewallace valentinewallace commented Nov 7, 2022

For context, LSPs need to be able to open 0-conf JIT channels to users upon the first time the user is receiving a payment. To do this, they will put fake route hints in end user invoices that signal to LDK that this is an intercept forward, similar to phantom payments. LDK will then generate an event, giving the LSP the opportunity to open the JIT channel. The LSP can then forward the payment over the newly opened channel, or fail it.

Based on #1840
Supercedes #1601

@valentinewallace valentinewallace force-pushed the 2022-11-jit-chan-htlc-intercept branch 5 times, most recently from beae304 to 90c4d75 Compare November 7, 2022 18:42
@valentinewallace valentinewallace mentioned this pull request Nov 7, 2022
@codecov-commenter
Copy link

codecov-commenter commented Nov 7, 2022

Codecov Report

Base: 90.70% // Head: 90.67% // Decreases project coverage by -0.02% ⚠️

Coverage data is based on head (a5c5d53) compared to base (440c3ee).
Patch coverage: 84.34% of modified lines in pull request are covered.

❗ Current head a5c5d53 differs from pull request most recent head acff8f6. Consider uploading reports for the commit acff8f6 to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1835      +/-   ##
==========================================
- Coverage   90.70%   90.67%   -0.03%     
==========================================
  Files          91       91              
  Lines       48090    48303     +213     
  Branches    48090    48303     +213     
==========================================
+ Hits        43618    43800     +182     
- Misses       4472     4503      +31     
Impacted Files Coverage Δ
lightning/src/util/config.rs 65.90% <0.00%> (-0.76%) ⬇️
lightning/src/util/events.rs 24.65% <4.16%> (-1.32%) ⬇️
lightning/src/ln/channelmanager.rs 86.20% <83.70%> (-0.16%) ⬇️
lightning/src/ln/payment_tests.rs 98.73% <97.74%> (-0.17%) ⬇️
lightning/src/util/scid_utils.rs 99.31% <100.00%> (+0.10%) ⬆️
lightning-block-sync/src/rest.rs 62.06% <0.00%> (-1.87%) ⬇️
lightning-block-sync/src/rpc.rs 74.80% <0.00%> (-1.14%) ⬇️
lightning-block-sync/src/poll.rs 86.33% <0.00%> (-0.58%) ⬇️
lightning-net-tokio/src/lib.rs 77.03% <0.00%> (-0.28%) ⬇️
lightning-block-sync/src/lib.rs 93.33% <0.00%> (-0.27%) ⬇️
... and 13 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

lightning/src/util/events.rs Outdated Show resolved Hide resolved
@TonyGiorgio
Copy link
Contributor

TonyGiorgio commented Nov 10, 2022

Are there plans for generic HTLC interception at all? Not dependent on the routing hints of someone else's invoice? There can still be logic into determining the type based on SCID but in general all HTLC's could still be intercepted or at least subscribed to based on type.

There are many use cases for generic HTLC interception that shouldn't depend on LDK development, here are a few:

  1. Other approaches to 0 conf JIT channel payments (something double-invoice like instead of routing hint-like)
  2. Escrow (having a router decide to continue routing to destination in order to deliver funds based on a condition)
  3. Whether or not to route payments down certain channels or at all (liquidity management, balance probe protection)
  4. Third party preimages (node not knowing the preimage but waiting for either a third party or a different application handle preimages)
  5. Hodl invoice (not advocating for long running but at least allowing for checks and processes to run for a few seconds)

You could still have SCID logic but I would love to see generic solved for first and then application specific tbh. I think that's worked really well in the LND/CLN ecosystems.

@valentinewallace
Copy link
Contributor Author

Are there plans for generic HTLC interception at all? Not dependent on the routing hints of someone else's invoice?

Yes, the plan is to swiftly follow up to this PR with adding general forward interception :)

(1), (4), and (5) are already supported via our PaymentReceived event, IIUC. LDK generates this event once all MPP parts arrive. Upon receipt, you can get the preimage and do whatever other desired operations you like, then call claim_funds (per the linked docs).

@valentinewallace
Copy link
Contributor Author

Had to rebase to get #1844.

@TonyGiorgio
Copy link
Contributor

Yes, the plan is to swiftly follow up to this PR with adding general forward interception :)

Awesome! Concept ACK from me then. I browsed through the code and the API usage sounds good to me but probably can't comment on the rest 👍

Copy link
Contributor

@ViktorTigerstrom ViktorTigerstrom left a comment

Choose a reason for hiding this comment

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

First look-through generally looks good :).

lightning/src/ln/channelmanager.rs Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
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.

I think we need a fallback failure on a timer. If the user screws up and loses the intercept event entirely we shouldn't just sit on the HTLC forever and get the channel force-closed.

lightning/src/util/events.rs Outdated Show resolved Hide resolved
lightning/src/util/events.rs Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
@valentinewallace
Copy link
Contributor Author

Great feedback once again @ViktorTigerstrom! Working on addressing it

@valentinewallace valentinewallace force-pushed the 2022-11-jit-chan-htlc-intercept branch 2 times, most recently from 0759af8 to ee70d50 Compare November 14, 2022 20:23
Copy link
Contributor

@ViktorTigerstrom ViktorTigerstrom left a comment

Choose a reason for hiding this comment

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

This looks good to me. Only real feedback I have left is that it would be valuable to include test coverage to ensure that we don't generate the PaymentIntercepted event + populate pending_intercepted_htlcs when accept_intercept_htlcs is set to false.

nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
commitment_signed_dance!(nodes[1], nodes[0], &payment_event.commitment_msg, false, true);

// Check that we generate the PaymentIntercepted event when an intercept forward is detected.
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be valuable to also have test coverage ensuring that the PaymentIntercepted event isn't generated + pending_intercepted_htlcs isn't populated when accept_intercept_htlcs is false.

Copy link
Contributor Author

@valentinewallace valentinewallace Nov 28, 2022

Choose a reason for hiding this comment

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

Gonna save that for follow-up for the sake of the size of this PR, but you can manually check that the test fails when the config change lines are commented out. FWIW, that would set a new standard of testing rigor for us since i.e. the zero-conf PR didn't/doesn't test its config.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok fair enough :)

lightning/src/ln/channelmanager.rs Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Show resolved Hide resolved
_ => unreachable!(),
};
timed_out_htlcs.push((prev_hop_data, htlc.forward_info.payment_hash,
HTLCFailReason::Reason { failure_code: 0x1000 | 14, data: Vec::new() },
Copy link
Collaborator

Choose a reason for hiding this comment

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

14 is definitely right here, but its an UPDATE error, so we need to include a channel_update for the outbound channel, which is obviously....unclear? I guess we could deliberately include the wrong channel's update here and use the previous-hop channel, but that's pretty nonsensical. Maybe we just temporary_node_failure?

@TheBlueMatt
Copy link
Collaborator

LGTM, I think.

@TheBlueMatt TheBlueMatt added this to the 0.0.113 milestone Nov 29, 2022
@ViktorTigerstrom
Copy link
Contributor

Looks good to me as well!

Copy link
Contributor

@dunxen dunxen left a comment

Choose a reason for hiding this comment

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

LGTM, just a tiny nit and question :)

payment_hash: PaymentHash,
/// How many msats were received on the inbound edge of this HTLC.
inbound_amount_msat: u64,
/// How many msats the payer intended to route to the next node. Depending on the reason you are
Copy link
Contributor

Choose a reason for hiding this comment

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

new rust style team pls save us

lightning/src/util/events.rs Outdated Show resolved Hide resolved
});

failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
HTLCFailReason::Reason { failure_code: 0x4000 | 10, data: Vec::new() },
Copy link
Contributor

Choose a reason for hiding this comment

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

not for this PR, but I've wondered whether we should collect failure code consts somewhere so it's easy to see what it is. I can create an issue if we'd want that, unless I missed some prior reasoning not to.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I think that would be nice to have!

/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
// TODO: when we move to deciding the best outbound channel at forward time, only take
// `next_node_id` and not `next_hop_channel_id`
pub fn forward_intercepted_htlc(&self, intercept_id: InterceptId, next_hop_channel_id: &[u8; 32], _next_node_id: PublicKey, amt_to_forward_msat: u64) -> Result<(), APIError> {
Copy link
Contributor

Choose a reason for hiding this comment

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

just a question to check my understanding:

Is this basically the procedure for an LDK user?

  1. Calls ChannelManager::get_intercept_scid
  2. Generates invoice with intercept scid in route hints for end-user
  3. Receives an HTLCIntercepted event when the HTLC is intercepted
  4. At this point the LDK user can create a channel
  5. Then call this method to forward over that JIT channel

Copy link
Contributor

@ViktorTigerstrom ViktorTigerstrom Nov 30, 2022

Choose a reason for hiding this comment

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

Yes that's my understanding, where the LDK user is the LSP in your example (nodes[1] in the test), and the end-user is nodes[2] in the test. I guess step (2) in your example doesn't necessarily need to be on the LDK user's side, as long as the Intercept scid is passed to the end-user.

Copy link
Contributor

@dunxen dunxen Nov 30, 2022

Choose a reason for hiding this comment

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

Ah, I should have read the test.

Yeah I think I phrased that weirdly for (2). End user generates invoice but just shoves the scid they got from the LSP in the route hints.

valentinewallace and others added 9 commits November 30, 2022 12:43
No htlcs are intercepted yet, that will be added in upcoming commit(s)

Co-authored-by: John Cantrell <[email protected]>
Co-authored-by: Valentine Wallace <[email protected]>
This is useful for LSPs who wish to create a just-in-time channel for end users
receiving a lightning payment. These fake scids will be encoded into route
hints in end user invoices, and signal to LDK to create an event triggering the
JIT channel, after which the payment will be received.

Co-authored-by: John Cantrell <[email protected]>
Co-authored-by: Valentine Wallace <[email protected]>
Used in upcoming commit(s) so users can intercept forwarded HTLCs

Co-authored-by: John Cantrell <[email protected]>
Co-authored-by: Valentine Wallace <[email protected]>
And store the pending intercepted HTLC in pending_intercepted_htlcs

Co-authored-by: John Cantrell <[email protected]>
Co-authored-by: Valentine Wallace <[email protected]>
See ChannelManager::forward_intercepted_htlc and
ChannelManager::get_intercept_scid for details

Co-authored-by: John Cantrell <[email protected]>
Co-authored-by: Valentine Wallace <[email protected]>
Co-authored-by: John Cantrell <[email protected]>
Co-authored-by: Valentine Wallace <[email protected]>
@valentinewallace
Copy link
Contributor Author

Rebased due to conflicts

Copy link

@ariard ariard left a comment

Choose a reason for hiding this comment

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

ACK acff8f6

Reviewed code changes since last diff, and also checked test coverage. Remaining comments are minor.

let next_hop_scid = match self.channel_state.lock().unwrap().by_id.get(next_hop_channel_id) {
Some(chan) => {
if !chan.is_usable() {
return Err(APIError::APIMisuseError {
Copy link

Choose a reason for hiding this comment

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

We have APIError::ChannelUnavailable in fact, could be used.

let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);

let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
.ok_or_else(|| APIError::APIMisuseError {
Copy link

Choose a reason for hiding this comment

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

Mutating this line does not break intercepted_payment

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.

LGTM, gonna land to get the conflicts out of the way but please address the two issues @ariard noted and the below one in a followup.

};
connect_block(&nodes[0], &block);
connect_block(&nodes[1], &block);
let block_count = 183; // find_route adds a random CLTV offset, so hardcode rather than summing consts
Copy link
Collaborator

Choose a reason for hiding this comment

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

It adds a random value in a range, though, so we should still be able to sum consts. Please change it to summing consts cause otherwise our entire test suite fails any time we change any constant :(

Note that if you use the get_route method it won't add the random value, as well.

@TheBlueMatt TheBlueMatt merged commit fb6e018 into lightningdevkit:main Dec 1, 2022
@TheBlueMatt TheBlueMatt mentioned this pull request Dec 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants