If the PSBT finalize step succeeds but the stream fails before
ChanPending, deposits would remain stuck in OpeningChannel until the
next daemon restart. Run the recovery logic immediately so deposits are
resolved without requiring a restart.
Also, add tests for the following edge cases requested in review:
- Reorg: channel tx reorged, UTXOs reappear as unspent, deposits
return to Deposited state.
- Daemon restart during channel opening: deposits in OpeningChannel
recovered based on UTXO status (spent → ChannelPublished, unspent →
Deposited).
- Mempool eviction: tx evicted, UTXOs unspent, deposits return to
Deposited.
- Mempool rejection: tx never accepted, same recovery as eviction.
- Stream errors: lnd stream fails before PSBT finalize, error returned
without errPsbtFinalized so deposits can be safely rolled back.
- PSBT finalize then stream abort: finalize succeeds but stream dies
before ChanPending, error wrapped with errPsbtFinalized so caller
triggers recovery instead of blind rollback.
- Duplicate outpoints: already covered by TestOpenChannelDuplicateOutpoints.
Duplicate outpoints in the request lead to fee miscalculation and an
invalid PSBT with the same input listed twice. Validate early and return
a clear error message.
Both OpeningChannel and ChannelPublished lacked OnExpiry transitions.
handleBlockNotification fires OnExpiry on every new block once the
deposit is expired, regardless of the current state. Since both states
use NoOpAction or FinalizeDepositAction which release the FSM mutex
briefly, an OnExpiry SendEvent can sneak in. Add self-transitions so
the event is safely absorbed.
Return an error instead of just logging when chainhash.NewHash fails in
the ChanPending handler. The hash variable would be nil and crash on the
subsequent String() call.
Protect the shimPending variable with a sync.Mutex since it is accessed
from multiple goroutines: the main loop goroutine and the server error
handling goroutine. Without synchronization this is a data race.
Block-based deposit fetching from the internal lnd wallet was
susceptible to wallet syncing issues. Replace it with interval-based
polling. Reconciliation errors are now logged instead of being fatal,
improving resilience during transient failures.
Remove unreachable error check after filterNewDeposits which does not
return an error. The err variable was already handled from the
ListUnspent call above and could never be non-nil at this point.
- reset htlcConfirmed when the HTLC conf subscription errors and we re-register
- add regression test ensuring re-registers after a conf error require a fresh
confirmation instead of sweeping on a stale one
Loop now guards all static-address managers against zero block heights: each
constructor returns an error when invoked with a non-positive current height,
and `loopd` validates the height from `GetInfo` before instantiating them.
Tests and helper code were updated accordingly so we fail fast instead of
registering chain notifications with invalid hints.
In this commit we add a new function SelectDeposits
to the loop-in manager. It coin-selects deposits that
meet an arbitrary swap amount provided by the client.
We have to ensure that the server creates the correct
change outputs for the htlc- and sweepless sweep
transactions.
since deposits have been normalized in the db in the
context of a static address swap this commit now
retrieves the deposit information as part of
GetLoopInByHash and GetStaticAddressLoopInSwapsByStates.
previously upon recovery, a withdrawing deposit was
first transitioned into the Deposited state by the
deposit manager, and then again into the Withdrawing
state by the withdrawal manager. The first transition
is unnecessary, so we just remain in the Withdrawing
state upon recovery.