-
Notifications
You must be signed in to change notification settings - Fork 2.3k
multi: resend shutdown on reestablish #8447
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
Changes from all commits
1bae43f
d58ffe1
ff3555c
8e191bb
f3be6c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -121,6 +121,11 @@ var ( | |||||||||||||||
| // broadcasted when moving the channel to state CoopBroadcasted. | ||||||||||||||||
| coopCloseTxKey = []byte("coop-closing-tx-key") | ||||||||||||||||
|
|
||||||||||||||||
| // deliveryScriptKey points to the delivery script to be sent along in | ||||||||||||||||
| // the Shutdown message. This is committed when moving the channel to | ||||||||||||||||
| // state ShutdownSent. | ||||||||||||||||
| deliveryScriptKey = []byte("delivery-script-key") | ||||||||||||||||
|
|
||||||||||||||||
| // commitDiffKey stores the current pending commitment state we've | ||||||||||||||||
| // extended to the remote party (if any). Each time we propose a new | ||||||||||||||||
| // state, we store the information necessary to reconstruct this state | ||||||||||||||||
|
|
@@ -188,6 +193,10 @@ var ( | |||||||||||||||
| // in the state CommitBroadcasted. | ||||||||||||||||
| ErrNoCloseTx = fmt.Errorf("no closing tx found") | ||||||||||||||||
|
|
||||||||||||||||
| // ErrNoDeliveryScript is returned when no closing delivery script has | ||||||||||||||||
| // been persisted for a channel. | ||||||||||||||||
| ErrNoDeliveryScript = fmt.Errorf("no delivery script") | ||||||||||||||||
|
|
||||||||||||||||
| // ErrNoRestoredChannelMutation is returned when a caller attempts to | ||||||||||||||||
| // mutate a channel that's been recovered. | ||||||||||||||||
| ErrNoRestoredChannelMutation = fmt.Errorf("cannot mutate restored " + | ||||||||||||||||
|
|
@@ -597,6 +606,11 @@ var ( | |||||||||||||||
| // ChanStatusRemoteCloseInitiator indicates that the remote node | ||||||||||||||||
| // initiated closing the channel. | ||||||||||||||||
| ChanStatusRemoteCloseInitiator ChannelStatus = 1 << 6 | ||||||||||||||||
|
|
||||||||||||||||
| // ChanStatusShutdownSent indicates that a shutdown has been initiated. | ||||||||||||||||
| // The ChanStatusLocalCloseInitiator and ChanStatusRemoteCloseInitiator | ||||||||||||||||
| // flags are used to determine which party initiated the shutdown. | ||||||||||||||||
| ChanStatusShutdownSent ChannelStatus = 1 << 7 | ||||||||||||||||
| ) | ||||||||||||||||
|
|
||||||||||||||||
| // chanStatusStrings maps a ChannelStatus to a human friendly string that | ||||||||||||||||
|
|
@@ -607,6 +621,7 @@ var chanStatusStrings = map[ChannelStatus]string{ | |||||||||||||||
| ChanStatusCommitBroadcasted: "ChanStatusCommitBroadcasted", | ||||||||||||||||
| ChanStatusLocalDataLoss: "ChanStatusLocalDataLoss", | ||||||||||||||||
| ChanStatusRestored: "ChanStatusRestored", | ||||||||||||||||
| ChanStatusShutdownSent: "ChanStatusShutdownSent", | ||||||||||||||||
| ChanStatusCoopBroadcasted: "ChanStatusCoopBroadcasted", | ||||||||||||||||
| ChanStatusLocalCloseInitiator: "ChanStatusLocalCloseInitiator", | ||||||||||||||||
| ChanStatusRemoteCloseInitiator: "ChanStatusRemoteCloseInitiator", | ||||||||||||||||
|
|
@@ -618,6 +633,7 @@ var orderedChanStatusFlags = []ChannelStatus{ | |||||||||||||||
| ChanStatusCommitBroadcasted, | ||||||||||||||||
| ChanStatusLocalDataLoss, | ||||||||||||||||
| ChanStatusRestored, | ||||||||||||||||
| ChanStatusShutdownSent, | ||||||||||||||||
| ChanStatusCoopBroadcasted, | ||||||||||||||||
| ChanStatusLocalCloseInitiator, | ||||||||||||||||
| ChanStatusRemoteCloseInitiator, | ||||||||||||||||
|
|
@@ -1589,6 +1605,83 @@ func (c *OpenChannel) isBorked(chanBucket kvdb.RBucket) (bool, error) { | |||||||||||||||
| return channel.chanStatus != ChanStatusDefault, nil | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // MarkShutdownSent updates the channel status to indicate that a shutdown has | ||||||||||||||||
| // been initiated. It also persists the delivery script that we will use in | ||||||||||||||||
| // the Shutdown message so that we can guarantee that the same delivery script | ||||||||||||||||
| // is used if a re-connect happens. | ||||||||||||||||
| func (c *OpenChannel) MarkShutdownSent(localDeliveryScript []byte, | ||||||||||||||||
| locallyInitiated bool) error { | ||||||||||||||||
|
|
||||||||||||||||
| return c.markShutdownSent(localDeliveryScript, locallyInitiated) | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // markShutdownSent is a helper function which modifies the channel status of | ||||||||||||||||
| // the receiving channel and inserts a delivery script under the delivery script | ||||||||||||||||
| // key. It adds a status which indicates the party that initiated the channel | ||||||||||||||||
| // close. | ||||||||||||||||
| func (c *OpenChannel) markShutdownSent(deliveryScript []byte, | ||||||||||||||||
| locallyInitiated bool) error { | ||||||||||||||||
|
|
||||||||||||||||
| c.Lock() | ||||||||||||||||
| defer c.Unlock() | ||||||||||||||||
ellemouton marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||
|
|
||||||||||||||||
| putDeliveryScript := func(chanBucket kvdb.RwBucket) error { | ||||||||||||||||
| return chanBucket.Put(deliveryScriptKey, deliveryScript) | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| status := ChanStatusShutdownSent | ||||||||||||||||
| if locallyInitiated { | ||||||||||||||||
| status |= ChanStatusLocalCloseInitiator | ||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can't add another channel status here because on restart, execution will hit this if statement and the link won't be loaded: Lines 881 to 882 in cf4f468
The reason the itest works is that coop close then proceeds via Lines 7821 to 7825 in cf4f468
It can also be observed because no Instead, we should just be able to use the existence of a delivery script to determine whether or not we need to continue coop close
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: I think we should adjust the checks that you're referencing @Crypt-iQ as opposed to refraining from updating statuses according to the events that transpire.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'd still need the checks for older nodes, so this would be adding a special case and duplicating some logic. Ultimately, loading the link with a non-
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue is that it isn't default though. We're definitely in a different state that isn't normal operation and can't accept new HTLC adds. Can you clarify what "the risk" is? I'm trying to weigh the cost of having increasingly fragmented logic (where consequences of changing stuff leaks like it is doing now) vs the cost of fixing things so the code straightforwardly reflects what is supposed to happen.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no issue with using
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there's a way to navigate this. For one, it's not really functioning as a "status" as it's a bit-vector. Instead it's a collection of flags. So really what the ChanStatusDefault check is doing is trying to check if specific flags are unset, and act accordingly. I think perhaps inverting the check and rather than checking "is it default", we should check "is it not have any one of these conditions". We can always push it to an extra field but I do think that the way we handle ChannelStatus right now is very fragile and should be reworked to be more structurally correct. Maybe an issue for a less tactical PR though. |
||||||||||||||||
| } else { | ||||||||||||||||
| status |= ChanStatusRemoteCloseInitiator | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| return c.putChanStatus(status, putDeliveryScript) | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // DeliveryScript returns the delivery script to use in the channel's Shutdown | ||||||||||||||||
| // message. ErrNoDeliveryScript is returned if no such delivery script has been | ||||||||||||||||
| // persisted yet. | ||||||||||||||||
| func (c *OpenChannel) DeliveryScript() ([]byte, error) { | ||||||||||||||||
ellemouton marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||
| var deliveryScript []byte | ||||||||||||||||
|
|
||||||||||||||||
| err := kvdb.View(c.Db.backend, func(tx kvdb.RTx) error { | ||||||||||||||||
| chanBucket, err := fetchChanBucket( | ||||||||||||||||
| tx, c.IdentityPub, &c.FundingOutpoint, c.ChainHash, | ||||||||||||||||
| ) | ||||||||||||||||
| switch { | ||||||||||||||||
| case err == nil: | ||||||||||||||||
| case errors.Is(err, ErrNoChanDBExists), | ||||||||||||||||
| errors.Is(err, ErrNoActiveChannels), | ||||||||||||||||
| errors.Is(err, ErrChannelNotFound): | ||||||||||||||||
|
|
||||||||||||||||
| return ErrNoDeliveryScript | ||||||||||||||||
| default: | ||||||||||||||||
| return err | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| delScriptBytes := chanBucket.Get(deliveryScriptKey) | ||||||||||||||||
| if delScriptBytes == nil { | ||||||||||||||||
| return ErrNoDeliveryScript | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // Make a copy of the returned address bytes here since the | ||||||||||||||||
| // value returned by Get is not safe to use outside of this | ||||||||||||||||
| // transaction. | ||||||||||||||||
| deliveryScript = make([]byte, len(delScriptBytes)) | ||||||||||||||||
| copy(deliveryScript, delScriptBytes) | ||||||||||||||||
|
|
||||||||||||||||
| return nil | ||||||||||||||||
| }, func() { | ||||||||||||||||
| deliveryScript = nil | ||||||||||||||||
| }) | ||||||||||||||||
| if err != nil { | ||||||||||||||||
| return nil, err | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| return deliveryScript, nil | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| // MarkCommitmentBroadcasted marks the channel as a commitment transaction has | ||||||||||||||||
| // been broadcast, either our own or the remote, and we should watch the chain | ||||||||||||||||
| // for it to confirm before taking any further action. It takes as argument the | ||||||||||||||||
|
|
||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.