-
Notifications
You must be signed in to change notification settings - Fork 492
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
option_simple_close (features 60/61) #1096
option_simple_close (features 60/61) #1096
Conversation
Pay your own fees, so the peer will sign without caring. Even if it doesn't relay. Signed-off-by: Rusty Russell <[email protected]>
The shutdown section says: ``` - MUST NOT send multiple `shutdown` messages. ``` But the reconnection section says: ``` - upon reconnection: - if it has sent a previous `shutdown`: - MUST retransmit `shutdown`. ``` So clearly, remove the former. Signed-off-by: Rusty Russell <[email protected]>
bfae229
to
84c4487
Compare
Perhaps this is a misunderstanding on my part for how the document is organized, but wouldn't you still have to support the original Otherwise I tend to like this. The game that has been identified where the closer of the channel pays the fee has the side effect of keeping channels open longer which I believe to be a good thing. ACK up to aa6deb8 |
There was an expressed desire in NY to more agressively clean up deprecated parts of the spec, even if they're still in use. It keeps the document cleaner, and avoid having to revisit later (when?) to remove the older parts. However, it's still in the git history. |
Yeah I understand the desire to remove deprecated stuff. I just wonder what a "valid" implementation looks like here. If we implement this change, and essentially drop support for the old closing scheme from the spec, doesn't that mean that Maybe this is more of a meta-question of who the main BOLTs are for. My naive (I'm new here!) perspective is that the BOLTs should be the document I should consult if I wanted to bootstrap a new LN impl and that as long as I implement everything in the main BOLTs without any of the optional stuff or extensions then I should be broadly compatible with the network. Through this lense, it would seem that this change would result in the removal of cooperative closes from the main specification. Is my understanding of the purpose of the spec wrong? If not, are we OK with removing coop closes from the table-stakes features of the lightning network? |
I'm not really sure who would be served by keeping the old stuff around - if someone is using the BOLTs to implement a fresh Lightning client, by the time they're done doing so (which probably takes a year!) hopefully everyone supports the new features and there's no need for them to even be aware the old crap was there. If the BOLTs exist purely for the engineers working on the existing implementations, we already have all the old code, making us read the old stuff while implementing the new stuff is a lot more confusing than just writing out the new stuff IME. I'm not sure who else the BOLTs "should be for". |
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.
Huge concept ACK, I'll really enjoy removing the old-style mutual close from eclair
😁
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.
My two cents on what we should keep inside the BOLTs.
I'd like to suggest cleaning up the feature that is somewhat outdated because having an outdated ways to do the same operation in the BOLTs can make it more challenging to read and understand what is the recent version.
If someone is writing a lightning node from scratch (and I promise that I will do it someday), they should start reading from the current state of the BOLT.
Of course, there is the concern of what happens if the BOLTs change while I am implementing the node? I think we all assume that an implementor will stay updated on the development of the spec (at least we hope).
|
||
Similarly, if the closer proposes a high fee, it doesn't harm the closee to sign the transaction, as the closer is paying. | ||
|
||
There's a slight game where each side would prefer the other side pay the fee, and proposes a minimal fee. If neither side proposes a fee which will relay, the negotiation can occur again, or the final commitment transaction can be spent. In practice, the opener has an incentive to offer a reasonable closing fee, as they would pay the fee for the commitment transaction, which also costs more to spend. |
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 discussion of the game theory hints at an issue I've always had with the "opener pays" rule. I do understand the pathological scenario that the rule is meant to avoid (where a malicious node can drain the funds of a victim node by repeatedly opening and abandoning channels to it), but this new proposal creates a perverse incentive.
Olav opens a channel to Kristján. Some time later, Kristján wants the channel closed. Under this new proposal, Kristján's fee-optimal channel-closing strategy would be to transmit a bogus error
message to Olav so that Olav will be forced (by protocol convention) to unilaterally close the channel. Thusly, Kristján would obtain an immediately spendable output and would avoid paying any fees.
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 always true.
But K can also mutual close at <1 sat per byte.
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.
How can any mutual closing transaction use <1 sat/vB fee rate? The transaction would not propagate.
And you're right that it's always true. It has always irked me. I swear I have seen some cases where nodes to which I have opened channels have sent me error
messages for no reason other than to get me to unilaterally close. I think it was a design mistake to have commitment transactions reward the non-closing party with an immediately spendable output. I think commitment transactions should time-lock both outputs to discourage baiting a peer into unilaterally closing.
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 will disappear once we have package relay, because at that point commitment transactions will pay no fees. The fees will have to be paid using CPFP on an ephemeral anchor. Also, if you send bogus error
s, nodes will start blacklisting you at some point and won't accept new channels from you: this isn't the case today, but will definitely be in the future.
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 opener should at least be on the hook for whatever the commitment fee is. Otherwise, as the non-opener, it's cheaper to FC and wait out the time lock. Also, if there's nothing on the non-opener side, is FC the only options since there's nothing on that side to pay the 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.
I want to challenge this idea of "it's cheaper to FC and wait out the time lock". Having money locked up carries opportunity cost of the entire output. Yes you may save in fees, but your outputs are tied up in chain resolution and that is a cost in its own right.
Also, if there's nothing on the non-opener side, is FC the only options since there's nothing on that side to pay the fee.
Why would a node be concerned about aggressively closing a channel that has no capital tied up in it?
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.
Current coop close is ~168 vB (2 x P2WPKH
outputs) to ~192 vB (2 x P2TR
outputs). Sweeping the timelock is 121-123 vB.. so ~35-50% smaller tx. I agree with the time cost, but if you are closing due to months of inactivity, what's another up to two weeks. But the opener would be doubly screwed since they would pay the FC (+ anchors) and then have to pay to sweep. I supposed the opener can "deter" this by keeping max commit fees low.
Closing channels should be discouraged, but having dead/inactive channel is worse.
In my mind, game theory would have me coop close at a very low fee if the balance is mostly on the other side or FC if it's mostly on my side and I'm not the opener and commit fee is high enough.
Why would a node be concerned about aggressively closing a channel that has no capital tied up in it?
More of a hypothetical.. how does a closer pay if there's little to nothing on their side.
I agree a change to channel closing is needed, but 100% opener to 100% closer is a pretty drastic change. I would like to see the opener using (some of) the commit fee already reserved and/or splitting the fee based on balance.
* Adds both directions to the diagram. * Rename closee_output to has_closee_output. * Refer explicitly to BOLT 3 for "null output" defn. * Pad the OP_RETURN to make 65 bytes.
More MUST, less SHOULD.
You have to give them something which will propagate. Signed-off-by: Rusty Russell <[email protected]>
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.
Concept ACK
| |--(3a)- closing_complete Fee->| | | ||
| |<-(3b)- closing_complete Fee--| | | ||
| |<-(4a)- closing_sig ----------| | | ||
| |--(4b)- closing_sig --------->| | |
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 doesn't seem like the typical use case where one side initiates the close and the other signs. Some explanation somewhere is needed:
- Is the idea here that both sides can contribute to the closing fee if they want?
- Does B's
closing_complete
override A's? And in that case, why does B sendclosing_sig
? - Did both A and B send
closing_complete
at the same time, so we have 2 flows happening at once?
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 is one flow happening in each direction, where the sender of closing_complete
is paying the fee.
Let me know if the proposed diagram here makes it easier to reason about.
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 is one flow happening in each direction, where the sender of
closing_complete
is paying the fee.
Do we really expect dual flows to happen like that in practice? If the other party offers to pay for the closing transaction, why would we want to send closing_complete
at all?
Let me know if the proposed diagram here makes it easier to reason about.
Your diagram helps to clarify corner cases but probably still needs some high-level explanation in the spec.
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 really expect dual flows to happen like that in practice?
I think we do, because each side should independently decide what fee they'd like to pay and immediately send closing_complete
(at least that's how I implemented it). Since the channel has already stopped relaying HTLCs at that point, both peers are likely eager to get their funds back asap to reinvest them into another channel.
#### Rationale | ||
Multiple signatures are given, so each side can choose whether it wants to include its own output. In the case of large fees and tiny channels, where neither side wants its output, the use of an OP_RETURN simply cleans up the dangling unspent transaction output. This is expected to be extremely rare. | ||
|
||
Note that there is usually no reason to pay a high fee for rapid processing, since an urgent child could pay the fee on the closing transactions' behalf. |
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 could also mention RBF of the closing transaction 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.
Also I fail to understand how we could ever have a transaction be urgent enough to push for confirmation if it involved burning all of the outputs in an OP_RETURN.
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.
Done in 8cfc619
Agreed at meeting that we should simply insist the larger output (or lower key as tiebreak) MUST have an output. I guess if both are dust we cannot close, then. |
You could still do this in the implementation potentially -- allow (If you did this, could you move the "has_closee_output" decision to be whether or not the closee set the sPK to In general, though, for a sufficiently small channel, you can't guarantee closure: a 100k sat channel closed to a single bare |
EDIT: This doesn't work well, since you don't know fee yet, you don't know whether you should discard: IGNORE Maybe this whole situation is simpler if we move "do I want an output?" negotiation to shutdown. This means we allow two new forms of scriptpubkey:
And now, you SHOULD only set an empty scriptpubkey if you have the lesser output (if both are equal, neither can). Note that the initial shutdown can happen while htlcs are still unsettled, but this will work itself out if you re-xmit shutdown afterwards, and have a rule not to send closing_complete if both sides opt for no output. |
…output. If both are dust, you should lowball fees. The next patch adds OP_RETURN as a valid shutdown scriptpubkey though if you really want to do this. This also addresses the case where people send a new `shutdown` with a *different* scriptpubkey. This could previously cause a race where you receive a bad signature (because it hasn't received the updated shutdown), so we ignore these cases. Signed-off-by: Rusty Russell <[email protected]>
OK, this time for sure! We only let one side (the "lesser in msats", if any) discard their output, so we always have one. We make the signature fields a tlv, so we can simply share them across the two messages, and carefully dictate which one to use. |
(Avoid @'s in commit msgs to reduce github notification spam? cf https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md ) |
So I'm finally ready for interop here @t-bast! One thing that came up during my last debugging session was the fact that we allow the I'm not sure how y'all handle this in other implementations, but in Before this new flow, the sequence was always set to the max possible sequence, which also disabled the RBF (which was want to enable) and also sequence locks semantics. However, if we allow a peer to set an arbitrary sequence, then they can set a sequence that looks like a past breached state (due to extraction of the state hint) or even a future unknown state. So I ended up modifying our implementation to only set/allow a sequence of IMO, there's no good reason to set your sequence to anything other than So what do y'all think about dropping the sequence field from the new messages? We'd then always restrict it to be |
🥳
We don't rely too much on the
You always sign mutual close transactions, so you can remember the corresponding
I think this is dangerous to rely on
Why restrict it? There are a lot of discussions of leveraging
I don't think this argument really holds: if your peer wants to do something funny that stands out, they have many other ways to do this anyway, restricting the |
We don't only rely on it, but you might not always have the In the past, we also based things purely on
How would you then handle the case of partial or complete data loss, but before that you signed a co-op close transaction, and that hit the chain? With the new protocol, do y'all also store very RBF'd close created by either party? Right now we only remember the very last one, as with the new flow, the other party can just continue to sign effectively useless updates to make the peer retain more state (soft DoS).
Can think of two reasons not to:
Any future protocol updates that repurpose the sequence for consensus rules would need to bump the transaction version, just like we did originally for the CSV package. If we follow the line of reasoning for allowing a custom sequence, then should we also allow a custom txn version? |
It simply falls into the "unknown transaction" case, where you cannot do anything and just let it confirm (which sends funds back to your wallet). I believe there is nothing to do in that case and it is harmless?
Yes, we store the
Since they have to increase the fees every time, that doesn't sound like a dangerous DoS (especially since you only need to store 32 bytes per attempt).
Those are reasonable reasons, I don't feel very strongly either way (especially since right now, we'll always set the |
This was discussed during yesterday's spec meeting, and there was rough consensus that the
Since we want the ability to set the @rustyrussell @Roasbeef does that sound good to you? |
Yes, this looks good... |
|
||
Note that there is usually no reason to pay a high fee for rapid processing, since an urgent child could pay the fee on the closing transactions' behalf. | ||
|
||
However, sending a new `shutdown` message overrides previous ones, so you can negotiate again (even changing the output address) if you want: in this case there's a race where you could receive a `closing_complete` for the previous output address, and the signature won't validate. In this case, ignoring the `closing_complete` is the correct behaviour, as the new `shutdown` will trigger a new `closing_complete` with the correct signature. This assumption that we only remember the last-sent of any message is why so many cases of bad signatures are simply ignored. |
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.
so you can negotiate again (even changing the output address) if you want
doesn't this override the safety feature of upfront shutdown?
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 is of course if you don't have upfront_shutdown
activated, we can make that clearer.
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.
Done in 8cfc619
@Roasbeef whether I think the behavior described in that comment made sense: when you receive a new However, I didn't consider taproot channels at all at that point: I'm wondering if the stricter requirement of renewing both local and remote
@rustyrussell there are a lot of pending comments on the PR that make it harder to make progress on testing cross-compatibility: if you don't have time to address those right now, do you want me to prepare a commit on top of your branch that tries to address these comments? This way you only have to review it instead of making the changes yourself. |
Closing in favor of #1205, as @rustyrussell asked me to take over that PR. |
This is a "can't fail!" close protocol, as discussed at the NY Summit, and on @Roasbeef's wishlist. It's about as simple as I could make it: the only complexity comes from allowing each side to indicate whether they want to omit their own output.
It's "taproot ready"(TM) in the sense that
shutdown
is always sent to trigger it, so that can contain the nonces without any persistence requirement.I split it into three commits for cleanliness:
I recommend reviewing it as separate commits, it'll make more sense!