-
Notifications
You must be signed in to change notification settings - Fork 3.4k
lnpeer: modern closing negotiation #7586
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
Conversation
e86198d to
0d27785
Compare
| except asyncio.exceptions.TimeoutError: | ||
| if not is_initiator and not their_fee: # we only force close if a peer doesn't reply | ||
| await self.lnworker.force_close_channel(chan.channel_id) | ||
| raise Exception("Peer didn't reply with closing signed, force closed.") |
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.
Note: the BOLT does not say that we have to force close.
I suppose this choice follows from https://github.com/lightning/bolts/pull/847/files#r689977518
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.
Note 2: _shutdown is called from two separate places, one of them is is a Taskgroup.
In that case, _shutdown could be cancelled if the remote closes the connection, and might not have time to force close.
This way of calling a task from non-homogeneous contexts seems dangerous. This comment is not about this specific pull request, but about how we expect code to be executed. A similar issue was encountered in request_force_close_from_backup
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 see, but I also don't recall why I added the force close here, what would you suggest?
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.
Sorry, I had not seen the new definition of "fail the channel". Indeed, we have to force close.
Maybe this should be using try_force_closing, as in #7645.
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.
- MUST fail the channel translates to force close the channel (lightning/bolts@eb6f308)
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.
Sorry, I had not seen the new definition of "fail the channel". Indeed, we have to force close. Maybe this should be using try_force_closing, as in #7645.
Saw your comment too late. We could wait for #7645 and then use self.try_force_closing, there are probably also some minor conflicts with this PR.
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.
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.
Also, the BOLT does not make a distinction between funder and non-funder. I think we have to force close in both cases.
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 don't need to wait for #7645. See the new API,
schedule_force_closingin 556b987
I'll use it, but I meant that we introduce a wrapper lnpeer.schedule_force_closing that is added and could be used here as well.
Also, the BOLT does not make a distinction between funder and non-funder. I think we have to force close in both cases.
My motivation was to catch the case where we would have sent an initial closing_signed message (as the initiator) and wait for the first response of the peer, if it doesn't, force close. I haven't tackled the case when the peer doesn't reply during a general step of the negotiation, of course we could close there as well.
|
Does the regtest test bring something more than the already existing unit tests? |
That is not what this PR is doing, it is sending the interval |
|
I think that our current use of user preferences in order to set This slider approach may or may not be used with the modern interval, but if we do not use it then we should not use feerates derived from user preferences. |
|
Regarding the choice of values, I think we could use the same fee rate as in the latest commitment (that is, until anchor outputs are supported). |
0d27785 to
da96c15
Compare
I think I was able to catch a bug by adding it, but we could remove it.
That may be an old comment for the specific value, it was just referring to the range definition problem.
I switched to ETA of 2 blocks, the fee interval seems reasonable, but I have to think about this again, as already some time has passed since the PR. |
da96c15 to
3c76c0e
Compare
| # if fee_satoshis is equal to its previously sent fee_satoshis: | ||
| if our_fee == their_fee: | ||
| # SHOULD sign and broadcast the final closing transaction. | ||
| break # we publish |
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.
The code on master calls send_closing_signed once before the while loop if we are the initiator, and once after the while loop if we are not the initiator. This ensures that the remote can publish the closing transaction. You seem to have replaced that with various calls inside the while loop. However, you do not call it everywhere. For example here, we break from the loop and no message will be sent. This creates a situation where the remote will try to force-close, because they do not receive our closing_signed. This correspond to what is described in the FIXME "the remote SHOULD send closing_signed, but some don't. ")
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 see that there could be a problem, but in this case I interpreted it that way that the peer would know that we have agreement and that we would publish (that's why we have the else clause in the timeout scenario). It seems like there's no instruction in the spec that we should reply again with closing_signed. We could send it for safety reasons.
|
I will try to rewrite the fee negotiation, I think it needs to be broken down into smaller functions |
Updates the closing fee negotiation to comply with most recent spec changes, see lightning/bolts#847 The closing negotiation is backwards compatible with the old negotiation.
3c76c0e to
ccd1f4b
Compare
Ok sure, the spec is kind of complicated and I tried to follow it as close as possible. I have even simulated the closing negotiation asynchronously with different parameters. |
electrum/lnpeer.py
Outdated
| # MUST propose a value "strictly between" the received fee_satoshis and its previously-sent fee_satoshis. | ||
| our_fee_proposed = (our_fee + their_fee) // 2 | ||
| if not (min(our_fee, their_fee) < our_fee_proposed < max(our_fee, their_fee)): | ||
| our_fee_proposed += (their_fee - our_fee) // 2 |
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 do not understand what this code is doing.
First, I suppose that the intent of this line is to set our_fee, not our_fee_proposed.
So, maybe you wanted to write something like our_fee = our_fee_proposed + (their_fee - our_fee)//2
Second, if abs(our_fee - their_fee) > 1, then (our_fee + their_fee) // 2 will always be strictly between the values, as explained in the comment on line 1892. Therefore, that if..else block seems unnecessary.
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, you're right, I think I experienced an off-by-one somewhere here and tried to fix it that way, it should be removed
| break | ||
| # SHOULD use `fee_satoshis` to sign and broadcast the final closing transaction | ||
| else: | ||
| our_fee = their_fee |
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.
this assignment is not needed.
also, if our_fee == their_fee, we break from the loop on line 1931, so I guess this branch is never executed.
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.
that's true, I was translating the spec proverbially (SHOULD use fee_satoshis)
|
I added a commit which may help with review to simulate different scenarios. I think it would be useful if we could design tests like that. |
|
closing in favor of #7680 |
Implements a recent spec change concerning channel closing lightning/bolts#847.
The new fee negotiation is based on fee ranges but is also backwards compatible to the incremental closing negotiation.
For testing purposes there's a configuration constant
MODERN_FEEto trigger the new behavior, which is on by default.I also added a new regtest test for collaborative channel closing.
Todo:
Future: