-
Notifications
You must be signed in to change notification settings - Fork 122
loopin: no timeout tx if invoice was paid #998
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
loopin: no timeout tx if invoice was paid #998
Conversation
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.
Summary of Changes
Hello @starius, 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 refines the loopin swap logic by preventing the publication of a timeout transaction when a swap's invoice has already been paid. This change optimizes the process by avoiding redundant on-chain transactions for already completed swaps. The underlying waitForSwapComplete function has been updated to reflect this new behavior, ensuring that if an HTLC times out but the invoice is settled, the function gracefully exits and marks the swap as successful, rather than attempting an unnecessary timeout transaction.
Highlights
- Conditional Timeout Transaction Publishing: The
waitForSwapCompletefunction is updated to prevent the publication of a timeout transaction if the associated invoice has already been paid, optimizing the swap completion process by avoiding unnecessary on-chain transactions. - Refined Swap Completion Logic: The main loop within
waitForSwapCompletehas been refactored to explicitly handle termination conditions, allowing the function to exit and mark the swap as successful (StateSuccess) if the invoice is finalized and the HTLC's CLTV expiry has been reached. - Improved State Variable Management: Key state variables (
htlcSpend,invoiceFinalized,htlcKeyRevealed) are now declared at a higher scope withinwaitForSwapComplete, enabling thepublishTxOnTimeoutclosure to accessinvoiceFinalizedand make informed decisions about publishing the timeout transaction.
Using Gemini Code Assist
The 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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.
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
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
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 aims to prevent publishing a timeout transaction for a loop-in swap if the corresponding invoice has already been paid. While the intention is correct, the implementation introduces a potential issue by using the invoiceFinalized flag. This flag is set to true for both settled and canceled invoices. This could lead to incorrect behavior where a timeout transaction is not published for a canceled invoice, and the swap is incorrectly marked as successful upon expiry. My review provides suggestions to use the more specific swap state loopdb.StateInvoiceSettled to fix this, ensuring the logic only applies to paid invoices.
loopin.go
Outdated
| if invoiceFinalized { | ||
| return 0, nil | ||
| } |
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.
Using invoiceFinalized here is problematic because it's also set to true when the invoice is canceled. This would prevent the timeout transaction from being published even if the invoice was canceled (not paid), which is incorrect. A better approach is to check the swap state, which is more specific.
I'll suggest a similar change for the loop exit condition below.
| if invoiceFinalized { | |
| return 0, nil | |
| } | |
| if s.state == loopdb.StateInvoiceSettled { | |
| return 0, nil | |
| } |
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 catch! Fixed.
loopin.go
Outdated
| if invoiceFinalized && s.height >= s.LoopInContract.CltvExpiry { | ||
| // Mark the swap successful. | ||
| s.setState(loopdb.StateSuccess) | ||
| break | ||
| } |
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.
Similar to my previous comment, using invoiceFinalized here can lead to incorrect behavior. If the invoice is canceled, invoiceFinalized becomes true, and this condition would incorrectly mark the swap as successful upon expiry. Checking for loopdb.StateInvoiceSettled ensures this logic only applies when the invoice has actually been paid.
| if invoiceFinalized && s.height >= s.LoopInContract.CltvExpiry { | |
| // Mark the swap successful. | |
| s.setState(loopdb.StateSuccess) | |
| break | |
| } | |
| if s.state == loopdb.StateInvoiceSettled && s.height >= s.LoopInContract.CltvExpiry { | |
| // Mark the swap successful. | |
| s.setState(loopdb.StateSuccess) | |
| break | |
| } |
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 catch! Fixed.
99b8966 to
1e92d87
Compare
1e92d87 to
4f9b4c5
Compare
|
/gemini review |
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 correctly prevents publishing a timeout transaction for a loop-in swap if the invoice has already been paid, even if the HTLC has expired. The changes achieve this by adding checks in publishTxOnTimeout and modifying the main loop in waitForSwapComplete to treat the swap as successful upon expiry if the invoice is settled. The logic is sound and also improves the loop termination condition. I have one suggestion to remove a redundant check.
|
@gemini-code-assist review |
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 modifies the loop-in swap logic to prevent publishing a timeout transaction when the swap invoice has already been settled. This is a sensible change, as the swap is effectively complete from the client's perspective once they've been paid. The implementation correctly adds checks to publishTxOnTimeout to prevent publishing. However, I've found a race condition in the refactored main loop in waitForSwapComplete. The new exit condition can cause the loop to terminate before processing the invoice settlement, which would lead to incorrect cost accounting for the swap. I've provided a suggestion to fix this issue.
loopin.go
Outdated
| if htlcSpend { | ||
| break | ||
| } |
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 change in the loop's exit condition introduces a potential race condition. The previous logic (for !htlcSpend || !invoiceFinalized) ensured that both the HTLC spend and the invoice finalization were processed before exiting the loop.
With the new condition if htlcSpend { break }, the loop can terminate as soon as the HTLC spend is detected (spendChan receives a value), even if the corresponding invoice update from swapInvoiceChan hasn't been processed. If the swap is successful, this can lead to incorrect accounting because the server cost (s.cost.Server) is calculated upon invoice settlement, and this update might be missed.
To prevent this race condition and ensure correct accounting, the loop should wait for the invoice to be finalized as well before breaking, similar to the original logic.
| if htlcSpend { | |
| break | |
| } | |
| if htlcSpend && invoiceFinalized { | |
| break | |
| } |
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.
Fixed.
Note that HTLC can't be spent by the server until the invoice is settled. And this code doesn't publish the timeout tx until the swap expires, which happens after the invoice expires.
However this fix doesn't harm, so let's have it.
This action is not needed, since the swap is complete. Wait until the timeout anyway to be able to push the key on every block.
4f9b4c5 to
a1d4ea9
Compare
hieblmi
left a comment
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.
Thanks! LGTM!
bhandras
left a comment
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, thank you! 🎉
This action is not needed, since the swap is complete. Wait until the timeout anyway to be able to push the key on every block.
Pull Request Checklist
release_notes.mdif your PR contains major features, breaking changes or bugfixes