Store the server's static loop-in confirmation-risk decision and the time it
was received. This lets recovered swaps reconstruct whether payment waiting had
already started and how much of the payment timeout remains.
Wire notification handling to persist accepted and rejected decisions before
caching and forwarding them. If the swap row is not present yet, the
notification is still cached so the per-swap waiter can replay and store the
decision later.
Recover accepted decisions by starting the payment deadline from the persisted
decision time, and recover rejected decisions by canceling the invoice and
failing the swap instead of waiting forever.
GetStaticAddressLoopInSwapsByStates passes a comma-separated
state list into a SQL LIKE membership check. The query wraps the
input with commas before matching latest update states as
comma-delimited tokens.
Wrapping that list in braces meant the first and last states
were not bounded by commas, so boundary entries in a state set
could be missed. In particular, Failed is the last final
static-address loop-in state, which made final-state queries skip
failed swaps.
Drop the braces from the serialized state list and extend the
SQL store test with a failed swap so the final-state boundary is
covered.
Add client handling for the server's static loop-in risk-rejected notification.
If the server aborts confirmation-risk waiting before payment, the client fails
the local swap instead of waiting for a payment deadline that will never start.
Cache rejected notifications by swap hash using the same replay path as accepted
notifications, and clear the opposite cached state when a final risk decision is
received. This keeps reconnect and subscription-order races from stranding the
client in the risk wait.
Wait for the server's static loop-in risk-accepted notification before starting
the client payment deadline. The server may intentionally hold the swap at the
confirmation-risk gate after HTLC signing, and the client deadline should not
run while that server-side wait is still in progress.
Cache risk-accepted notifications by swap hash inside the local notification
manager and replay them to the per-swap subscriber. This covers both reconnects
and the internal race where the global notification stream receives the server
event before the static loop-in FSM registers its waiter.
Keep replacement UTXOs as fresh deposits while preserving the original deposit
record and selected outpoint snapshot for pending swaps.
Before signing a static loop-in HTLC, check each original selected outpoint with
GetTxOut(..., includeMempool=true). Cancel the pending invoice only when that
check reports an original outpoint unavailable; lookup errors fail the action
without canceling so transient chain backend errors do not incorrectly abandon
the swap.
Keep recovered loop-ins using their stored outpoint snapshot and cover
replacement discovery and cancellation in tests.
If InitHtlcAction creates the private swap invoice but fails before the loop-in
is stored, the retry path otherwise leaves behind a live orphan invoice.
Cancel that invoice on the early error path with a detached, timeout-limited
context, and reuse the same helper when tearing down the monitor path. This
keeps failed initialization attempts from leaving invoices that no local swap
can complete.
Allow static loop-ins to select unconfirmed deposits because their CSV timeout
has not started yet, while still preferring confirmed outputs during automatic
selection.
Keep confirmed-input requirements for channel opens and withdrawals now that
Deposited includes mempool outputs. Filter unconfirmed deposits out of automatic
selection for those flows and fail manual requests that reference them, so the
client does not build PSBTs or withdrawal attempts with unusable inputs.
Treat deposit.MinConfs as the legacy readiness threshold rather than the single
source of truth for all flows. Loop-in readiness is now governed by server
confirmation-risk policy, while withdrawals and channel opens keep their
confirmed-input checks.
Replace the recursive full-deposit autoloop selector with a bounded-memory
DP implementation in staticaddr/loopin/autoloop_dp.go. The new selector
keeps the existing no-change semantics, first finds the best reachable
total, then applies the 25 percent band rule so earlier-expiring deposits
can win inside that near-optimal range.
The DP table is capped at 128 MiB and keeps exact satoshi sums alongside
compressed bucket weights, so planning stays memory-bounded without
allowing oversized candidates. The compressed weighting now rounds down
with a minimum of one bucket, which avoids rejecting valid sums after
multiple per-deposit rounding steps while leaving the exact-sum check
as the real safety boundary.
Teach the liquidity manager to include persisted static loop-ins
in budget accounting, in-flight limits, and peer traffic backoff.
This adds the static fee model used for conservative accounting
and passes storage errors through the relevant planner helpers.
The daemon wiring now exposes static loop-ins to liquidity so the
manager can see the same ongoing swaps that the static-address
subsystem persists, while easy autoloop keeps working with the new
fallible traffic lookup path.
Add the static-address helper that prepares full-deposit autoloop loop-ins
without dispatching them. The helper selects no-change deposit sets, records
explicit outpoints, and quotes the exact selected amount before the planner
tries to dispatch anything.
The tests cover the full-deposit selector, the quoted request construction,
and excluded outpoint handling so later liquidity work can rely on a stable
preparation surface.
Move static loop-in label validation to the rpc boundary and
remove the same check from the internal manager path.
This keeps external requests aligned with the existing swap rpc
surface while allowing internal autoloop callers to keep using
reserved labels for automated swaps. The tests cover both sides of
that contract: rpc requests still reject reserved labels, and the
manager path accepts them.
The Parameters struct describes the keys, expiry and pkScript that
define the static address script, so its natural home is the script
package. Moving it there lets staticutil drop its dependency on the
address package and lets callers reuse a single type alongside
script.StaticAddress and script.NewStaticAddress.
No behavior change.
Closes#1056
Reduce MinConfs from 6 to 3 to allow faster swap attempts while the
server enforces risk-based confirmation requirements. Update
SelectDeposits to prioritize more-confirmed deposits first, increasing
the likelihood of server acceptance. Add client-side logging of
insufficient confirmation details from server error responses.
Remove unused errChan fields from the loopin, openchannel, and withdraw
managers. These channels were declared and initialized but never read
from or written to.
Remove the unused activeLoopIns map from the loopin manager. The map
was only written to but never read, making it dead code.
Remove the stale withdraw.Store interface whose method signatures no
longer match the concrete SqlStore API used by the manager.
Remove unused config fields from openchannel.Config (Server,
AddressManager, ChainNotifier, Signer) and deposit.ManagerConfig
(AddressClient, SwapClient, ChainParams) along with their daemon
wiring. Also remove the now-orphaned openchannel.AddressManager
interface.
Remove the unused GetStaticAddress and Close methods from
address.SqlStore and the GetStaticAddress method from the address.Store
interface, as the codebase only uses GetAllStaticAddresses.
recoverLoopIns wrote to the activeLoopIns map from inside a goroutine
without any synchronization. The map is shared state accessed from
the manager's main Run loop, creating a data race.
Move the map write before the goroutine spawn so it happens
synchronously during recovery. Also fix the stale log message that
said "OnStart" instead of "OnRecover".
SweepHtlcTimeoutAction used a select with a default case containing a
blocking time.After. When the context was canceled, it logged the error
but continued the retry loop instead of returning. The default case
also meant that ctx.Done was only checked when it was already signaled,
while an hour-long sleep blocked without listening for cancellation.
Replace the default+time.After pattern with a proper select on both
ctx.Done and time.After so the function exits promptly on shutdown.
createHtlcTx always places the HTLC output at index 0 and the change
output (if any) at index 1. Previously, createHtlcSweepTx attempted to
dynamically find the HTLC index but then unconditionally read
TxOut[0].Value, ignoring the computed index.
Replace the dynamic search with a const htlcInputIndex=0 and a fail-fast
check that errors if the layout invariant is ever violated. Add a test
that verifies the sweep value is derived from the HTLC output, not the
change output.
- 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.
If the notification manager shuts down, sweepReqs channel is closed and sweepReq
object received from it becomes nil, causing panic in handleLoopInSweepReq. Now
this is fixed by checking if the channel was closed and if it was shutting down
loop-in manager itself.