-
Couldn't load subscription status.
- Fork 2.2k
Aux Closer: Move coop-close aux finalization to chain watcher #10289
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
base: master
Are you sure you want to change the base?
Conversation
Due to a circular dependency issue, we cannot directly import these definitions from lnwallet/chancloser. So we'll redefine the types we need and define a slim interface around the aux closer, which contains exactly the functionality that we'll want to use here.
We now execute the aux chan closer finalization within the chain watcher. This is better as we don't need to rely on the remote party being online and sending us a message. Instead we do the finalization once the on-chain transaction has been confirmed.
The final step is to provide the aux closer to the corresponding configs from the server. Here we use an adapter pattern which returns an aux chan closer as defined in the contractcourt package.
Summary of ChangesHello @GeorgeTsagk, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request refactors the cooperative channel closing mechanism to enhance its reliability. It shifts the responsibility of finalizing auxiliary channel closures from relying on a peer's message to monitoring the blockchain for the confirmed cooperative close transaction. This change ensures that finalization is guaranteed, albeit with a slight delay, by removing the dependency on the peer's online status or correct behavior. The implementation also addresses a resulting circular dependency by introducing new types and an adapter. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
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.
Code Review
This pull request refactors the cooperative close finalization by moving the AuxChanCloser hook from ReceiveClosingSigned to the chainWatcher. This is a good improvement as it makes the finalization process more reliable by depending on on-chain events rather than peer messages.
The implementation of the adapter pattern to resolve the circular dependency is clean. However, I've found a critical issue in the new finalizeCoopClose function where the logic to identify local and remote outputs is incorrect and could lead to problems if there are more than two outputs in the closing transaction. Additionally, some fields in AuxCloseDesc were not being populated. I've provided a detailed comment and a code suggestion to address this.
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 did a first pass on this PR and left some comments.
There's also a typo in the second commit: countractcourt+lnwallet should be contractcourt+lnwallet
I have a few general questions as well:
If node restarts after close tx confirms but before finalization, what happens? Do we re-detect the spend on restart?
I'm missing an unit test for finalizeCoopClose., although the original ReceiveClosingSigned func also doesn't have any testing iiiuc. But this might be a good time to increase that coverage.
| // polling the database for a channel's commitpoint. | ||
| maxCommitPointPollTimeout = 10 * time.Minute | ||
| ) | ||
|
|
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.
Consider refactoring these types into a shared package. e.g: lnwallet/chantypes
This would avoid the duplication and the circular dependency. 🦅 🦅 🪨💨
| return fmt.Errorf("get shutdown info: %w", err) | ||
| } | ||
|
|
||
| // TODO(roasbeef): Extract the internal key if this is a taproot |
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.
Why is this a todo specifically for roasbeef and not for @GeorgeTsagk?
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 todo was meant for @GeorgeTsagk , but realized now it is not needed at all. Will update comments accordingly
| } | ||
| c.closingTx = closeTx | ||
|
|
||
| // If there's an aux chan closer, then we'll finalize with 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.
question: is there value in leaving this code?
This PR introduces more robust handling of the aux closer finalization by moving the hook call site from ReceiveClosingSigned to the chainwatcher. That also introduces a waiting time of ~10 minutes before the finalization is initiated. Couldn't we keep doing both?
In case our peer stays online this would lead to a faster finalization which in turn maybe is a better user experience.
| // state determines which privileges the peer has with our server. | ||
| state peerAccessStatus | ||
| } | ||
|
|
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.
Maybe consider moving to a dedicated file like contractcourt/aux_closer_adapter.go for better organization. server.go is so long already.
|
Replying to @gijswijs's comment:
I believe that this is the case, since this is code that is called for any channel in LND (with the
Yeah there's not much to test as this is an external hook. We can make a basic-level assertion on a mock implementation of it -- i.e just make sure that it is being called with non-nil parameters. |
|
@Roasbeef: review reminder |
| } | ||
|
|
||
| // AuxCloseDesc is used to describe the channel close that is being performed. | ||
| type AuxCloseDesc struct { |
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.
These types are duplicated from lnwallet/chancloser/aux_closer.go. Why do they need to be moved here?
| return fmt.Errorf("get shutdown info: %w", err) | ||
| } | ||
|
|
||
| // TODO(roasbeef): Extract the internal key if this is a taproot |
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.
?
| var localCloseOutput, remoteCloseOutput fn.Option[CloseOutput] | ||
|
|
||
| // Get the delivery scripts for both parties. | ||
| var localDeliveryScript lnwire.DeliveryAddress |
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 comment says both parties, but lonly extracts this script for a single party.
|
|
||
| // Get the delivery scripts for both parties. | ||
| var localDeliveryScript lnwire.DeliveryAddress | ||
| shutdown.WhenSome(func(s channeldb.ShutdownInfo) { |
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.
IIUC this must be set at this point. We can encode this invariant by returning an error if it isn't available.
| // TODO(roasbeef): Extract the internal key if this is a taproot | ||
| // channel. For now, we leave it as None since we don't persist it | ||
| // separately from the delivery script. | ||
| var internalKey fn.Option[btcec.PublicKey] |
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 existing call site, this is always properly set.
| RemoteCloseOutput: remoteCloseOutput, | ||
| } | ||
|
|
||
| // Call FinalizeClose on the aux closer. |
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.
Superflous comments here and above.
| } | ||
|
|
||
| // FinalizeClose adapts the chancloser types to contractcourt types. | ||
| func (a auxCloserAdapter) FinalizeClose(desc contractcourt.AuxCloseDesc, |
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.
Why do we need this adapter at all? Why can't we pass the existing interface as defined in the chanclose package into the chain watcher?
Description
Moves the aux closer finalization hook call site from
ReceiveClosingSigned, which relies on our peer staying online and sending us the message, to the chain watcher where we instead wait for the coop close tx to be detected onchain.This implies that the finalization will be completed after some delay, but this also comes coupled with the guarantee of not having to rely on our peer behaving correctly.
Circular dependency
The aux closer is used in
lnwallet/chancloserand now also incontractcourt(by chain watcher). This caused a circular dependency which disallowed us from importing some definitions directly from the former package. The direct solution was to replicate some of the types and use an adapter on the server for our new slim interface. Not an ideal solution, will keep searching for alternatives -- although this is non blocking for the purpose of this PR.