-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Conversation
d899502
to
54852b9
Compare
There was a problem hiding this 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?
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.
If we have auto-release, then |
91fc959
to
0a67ab2
Compare
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. |
There was a problem hiding this 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.
log.Errorf("Error handling intercepted htlc "+ | ||
"that expires too soon: circuit=%v, "+ | ||
"incoming_timeout=%v, err=%v", | ||
packet.inKey(), packet.incomingTimeout, err) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
0a67ab2
to
9d32265
Compare
9d32265
to
cf0bb13
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🥇
Fyi the inteceptor tests have a data race (also in this PR), ptal #6353 |
cf0bb13
to
9ab7d2c
Compare
There was a problem hiding this 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( |
There was a problem hiding this comment.
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 👍
There was a problem hiding this comment.
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 😅
9ab7d2c
to
83a940c
Compare
@@ -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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
83a940c
to
53265fc
Compare
@Roasbeef: review reminder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🏒
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.
53265fc
to
13dff2f
Compare
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.