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

htlcswitch: interceptor expiry check #6212

Merged
merged 2 commits into from
Apr 12, 2022

Conversation

joostjager
Copy link
Contributor

Adds a basic protection to make sure that we aren't letting the interceptor handle htlcs that are too close to the channel force-close broadcast height.

Copy link
Collaborator

@carlaKC carlaKC 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 its worth thinking about the full solution for the expiry handling that we want for the interceptor here. Specifically, the case where the interceptor holds on to a htlc for too long (risking force close), similar to hold invoices.

I think it would make sense to make the delta configurable through the interceptor api or add a force_release_height field to intercepted hltcs so that calling code knows when they'll just be dropped to prevent a force close. I would lean towards the latter, since I imagine most people will just use the sane default we set?

server.go Show resolved Hide resolved
@joostjager
Copy link
Contributor Author

I think its worth thinking about the full solution for the expiry handling that we want for the interceptor here. Specifically, the case where the interceptor holds on to a htlc for too long (risking force close), similar to hold invoices.

Not having the full solution is probably asking for trouble indeed. Especially in combination with #6165, force-closes will already happen when the interceptor is only down for too long.

I think it would make sense to make the delta configurable through the interceptor api or add a force_release_height field to intercepted hltcs so that calling code knows when they'll just be dropped to prevent a force close. I would lean towards the latter, since I imagine most people will just use the sane default we set?

If we have auto-release, then force_release_height sounds like a useful addition.

@Roasbeef Roasbeef added this to the v0.15.0 milestone Jan 28, 2022
@Roasbeef Roasbeef added htlcswitch P1 MUST be fixed or reviewed bug fix labels Feb 14, 2022
@joostjager joostjager force-pushed the interceptor-check branch 2 times, most recently from 91fc959 to 0a67ab2 Compare March 22, 2022 08:31
@joostjager
Copy link
Contributor Author

joostjager commented Mar 22, 2022

Rebased this PR after the merge of #6232.

I know that ideally we'd like to have more functionality for expiry watching, but this PR is the minimum required for short-hold interception applications to allow those applications to not worry about expiration.

@joostjager joostjager requested a review from carlaKC March 22, 2022 08:59
Copy link
Collaborator

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

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

Happy with minimal approach to start with. Just one error handling question from me.

htlcswitch/interceptable_switch.go Outdated Show resolved Hide resolved
log.Errorf("Error handling intercepted htlc "+
"that expires too soon: circuit=%v, "+
"incoming_timeout=%v, err=%v",
packet.inKey(), packet.incomingTimeout, err)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we want to proceed here if we errored out/did not handle (ie FailWithCode errored in handleExpired), shouldn't we return false?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good one, I indeed forgot a return statement there.

Interesting that you say return false, because I was thinking of returning true. With return false, the packet is passed on to the switch as normal and this violates the always-on requirement, if that is configured. Returning true isn't ideal either, because the htlc will remain stuck.

Maybe this is all hypothetical, because there is no way for an error to happen unless there is a bug in the code?

Copy link
Collaborator

Choose a reason for hiding this comment

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

In the hypothetical case, isn't it better to release to the switch and let it handle the expiry accordingly? Chances are that there's nowhere to forward it onwards (since generally there's no next hop when intercepting), so it'll fail back anyway?

this violates the always-on requirement

always on vs stuck htlc, difficult call

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whether there is nothing to forward onwards depends on the application I think. For external invoice handling it is true, but I think the interceptor is also used for the interception of regular routed traffic?

I think that one can come up with a high stakes htlc case where a stuck htlc and force-closed channel is preferable to skipping the interceptor.

But as this error path should be possible in theory only, I am fine with returning false. Updated.

Copy link
Collaborator

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

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

🥇

peer/test_utils.go Outdated Show resolved Hide resolved
htlcswitch/switch_test.go Outdated Show resolved Hide resolved
htlcswitch/interceptable_switch.go Show resolved Hide resolved
@bhandras
Copy link
Collaborator

Fyi the inteceptor tests have a data race (also in this PR), ptal #6353

@joostjager
Copy link
Contributor Author

Fyi the inteceptor tests have a data race (also in this PR), ptal #6353

I think I didn't add more of those in this PR. Didn't rebase, but hope for a clean auto-merge after #6353 is merged.

Copy link
Collaborator

@bhandras bhandras left a comment

Choose a reason for hiding this comment

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

LGTM 🎉


// Simulate an error during the composition of the failure message.
currentCallback := s.cfg.FetchLastChannelUpdate
s.cfg.FetchLastChannelUpdate = func(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice that the error is so simple to fake 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, got away cleaner than expected 😅

peer/test_utils.go Outdated Show resolved Hide resolved
@@ -55,6 +55,10 @@ type InterceptableSwitch struct {
// holdForwards keeps track of outstanding intercepted forwards.
holdForwards map[channeldb.CircuitKey]InterceptedForward

// cltvRejectDelta defines the number of blocks before the expiry of the
// htlc where we no longer intercept it and instead cancel it back.
cltvRejectDelta uint32
Copy link
Member

Choose a reason for hiding this comment

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

We already have a value for this in OutgoingCltvRejectDelta, so no need to make a new value here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, OutgoingCltvRejectDelta is passed around in several structures, but afaik it isn't directly accessible here. Or do you suggest to hardcode it to lncfg.DefaultOutgoingCltvRejectDelta here?

Copy link
Member

Choose a reason for hiding this comment

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

Was thinking that we should use DefaultOutgoingCltvRejectDelta but then saw that we use the final CLTV delta here, which I think is more apt as in most cases the intercepted HTLC will be terminated by the program operating the HTLC interceptor.

@lightninglabs-deploy
Copy link

@Roasbeef: review reminder

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

LGTM 🏒

@Roasbeef
Copy link
Member

This should be good to land after a rebase to clear up the conflicts.

Decouple the tests somewhat and fix a bug along the way where the test
passed because of a left-over package from a prior test.
@guggero guggero merged commit 8047f23 into lightningnetwork:master Apr 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug fix htlcswitch P1 MUST be fixed or reviewed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants