lightninglabs / lightninglabs/loop
staticaddr/loopin: make canceled-invoice timeout unlock recoverable
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 595
- Forks
- 135
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 12
Description
Problem
A static-address loop-in can reach the canceled-invoice recovery path without a durable guarantee that its deposits were unlocked.
Current monitor flow:
-
the payment deadline path cancels the invoice and then unlocks deposits:
cancelInvoice() err = f.unlockDeposits(ctx) -
those are separate side effects. If
loopdshuts down, crashes, or the deposit transition fails after invoice cancellation succeeds but before deposits are moved back toDeposited, recovery seesLookupInvoicereturnContractCanceled; -
in that recovered state no new
deadlineChanis created, so the deadline unlock path is not retried; -
if the HTLC never confirmed and the CLTV height arrives,
MonitorInvoiceAndHtlcTxActionreturnsOnSwapTimedOutand the loop-in moves toFailed, while deposits may remain stuck inLoopingIn.
This was discussed during PR #1161: https://github.com/lightninglabs/loop/pull/1161#discussion_r3539797010
The issue appears to predate PR #1161, but that PR touched the surrounding recovery/lifecycle code and made the invariant visible.
Potential fix
Route the no-HTLC-confirmation timeout path through a recoverable state-machine unlock state instead of transitioning directly to Failed.
One possible shape:
- add a success event such as
OnDepositsUnlocked; - route
MonitorInvoiceAndHtlcTx -> OnSwapTimedOut -> UnlockState(or a more specificUnlockDepositsBeforeFailstate); - make
UnlockStateretry onOnRecover; - only transition
UnlockState -> OnDepositsUnlocked -> FailedafterunlockDepositssucceeds; - if
unlockDepositsfails, keep the loop-in in the unlock state so restart/recovery retries cleanup rather than marking the swap failed with deposits possibly still locked.
The important invariant is: a no-confirmation timeout should not become final Failed until deposit unlock has succeeded or is known to be unnecessary.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with MonitorInvoiceAndHtlcTxAction and the MonitorInvoiceAndHtlcTx transition on OnSwapTimedOut, then inspect UnlockState, OnRecover, and unlockDeposits. Review the recovery and lifecycle discussion in PR #1161. Done means a no-confirmation timeout remains recoverable and reaches Failed only after deposits are unlocked or known to be unnecessary.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend, payments
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100