Delay the static address summary RPC until after validation and quote fee
checks. This keeps early error paths on their previous gRPC flow.
Add summary RPC events to the static loop-in sessions that reach the
confirmation prompt or swap dispatch, where the CLI needs the expiry
horizon for low-confirmation warnings.
Risk accepted and rejected notifications had nearly identical parsing,
persistence, cache update, and subscriber fanout code. The duplication made it
easy for the two decision paths to drift.
Introduce a shared risk-decision handler that validates the swap hash, persists
the decision, updates the matching cache, clears the opposite cache, and fans
out only to the subscriber for that swap.
Keep risk-decision delivery best-effort for slow subscribers, while queued
delivery remains limited to notification types that must not be dropped. Fold
the queue tests through a common helper so both queued notification paths keep
the same behavior.
Some notification types are work requests and must not be dropped when a
subscriber channel is temporarily full. Sending them synchronously from the
global notification handler can stall the stream while a slow subscriber holds
back reads.
Add a per-subscriber queue for blocking notification classes and deliver those
notifications from a worker tied to the subscriber context. Keep direct sends as
the fallback for subscribers that do not need queued delivery.
Use the queued path for static loop-in sweep requests and unfinished swap
notifications, and fix subscriber removal to compare channel identity now that
subscribers contain function fields.
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.
ListUnspentDeposits now reports only wallet UTXOs that have an active Deposited
record. That matches the static loop-in admission path and avoids exposing
wallet-seen outputs that are not ready for loop-in selection.
Make local notification fan-out non-blocking for best-effort categories so a
slow subscriber cannot stall the notification manager while it holds the
subscriber lock. Static loop-in sweep signing requests remain blocking because
they are work requests required for sweepbatcher presigning and must not be
dropped.
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.
FinalizeDepositAction only needs to tell the manager to remove the FSM from its
active set, but the old synchronous send was still tied to the caller context
and could race with request cancellation or a busy manager loop.
Send the cleanup notification asynchronously and tie it to the FSM lifetime
instead. Withdrawal completion no longer blocks while deposit locks are held
just because the original request context was canceled.
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.
Remove the old "no confirmed deposits available" error now that mempool
deposits are listed immediately and can be selected for static loop-ins.
Reproduce the server static-address deposit selection order in the CLI using
the already-returned deposit metadata. This keeps the low-confirmation warning
focused on the deposits auto-selection would actually choose, so users only see
it when the swap payment may wait for the server confirmation-risk policy.
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.
The deposit manager consumes one block epoch before recovered deposit FSMs are
started. That left already-expired recovered deposits idle until another block
arrived.
Remember the startup height and deliver it to active deposit FSMs after
recovery and reconciliation have finished. Move the block notification fanout
into a helper so startup replay and normal block handling use the same path.
Add coverage that starts the manager with a recovered deposit at its expiry
height and verifies the expiry sweep is signed and published immediately.
Surface static-address deposits as soon as they appear in the wallet instead
of waiting for the old six-confirmation readiness threshold.
Reconcile the wallet view on startup, on each block, and on the polling ticker
so mempool deposits are created immediately. Backfill the first confirmation
height once those outputs confirm, protect unconfirmed deposits from expiry,
and mark vanished unconfirmed outpoints as Replaced so RBFed-away deposits stop
showing up in RPCs.
Expose the new state through static-address RPCs by deriving availability and
summary totals from stored deposit state, reporting sensible expiry data for
unconfirmed outputs, and hiding Replaced records from normal listings.
Add NotificationTypeHtlcConfirmed and SubscribeHtlcConfirmed so consumers
can subscribe to HTLC-confirmed notifications. Also add dispatch in
handleNotification to forward htlc_confirmed messages to subscribers.
Add ServerHtlcConfirmedNotification message to SubscribeNotificationsResponse
oneof. This new notification type allows the server to inform the client that
a loop out HTLC has been confirmed on-chain, including the confirmed outpoint.
Loop-in and loop-out responses carry compressed server public keys that are copied into fixed-size fields and later used for HTLC construction. Validate the length and parse each compressed key before storing it, and validate the MuSig2 loop-in receiver internal key as well.
This turns short or unparsable server keys into explicit errors instead of silently zero-padding short responses or accepting an invalid internal key. Update root test mocks to return size-correct MuSig2 signing data under the stricter checks.
The cooperative batch sweep path receives a server nonce and partial signature before constructing a keyspend witness. Validate both byte slice lengths before registering the nonce or combining signatures, so malformed server responses fail explicitly instead of being zero-padded into fixed-size MuSig2 buffers.
Update batcher test helpers to return size-correct placeholder signing data under the stricter validation.
Server-supplied nonces and partial signatures are consumed by the static address loop-in and withdrawal MuSig2 signing paths. Reject nil signing info, wrong nonce lengths, and wrong partial signature lengths before registering nonces or combining signatures, so malformed responses cannot be silently zero-padded into signing attempts.
Add withdrawal coverage for nil and malformed server signing data.
GitLab probably blocked GitHub's CI runner IP address. So we can't download
packages from GitLab directly during this check. Workaround applied: download
these particular packages from proxy.golang.org which works in CI.
Add recorded CLI coverage for selecting the static-address autoloop
loop-in source, displaying the resulting parameters, and showing a static
loop-in suggestion. Also record the non-experimental rejection so the
opt-in gate is covered.
Update existing liquidity fixtures with the default loop-in source field
emitted by the newer RPC shape.
Static-address loop-ins in autoloop are still experimental. Reject
loop_in_source=static-address at the RPC boundary unless loopd was started
with --experimental, and pass the same opt-in into the liquidity manager so
persisted params cannot bypass the gate after restart.
The existing static swap accounting remains wired through the manager; the
gate only controls accepting and planning new static-address autoloops.
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.
Static autoloop testing surfaced a SuggestSwaps failure when the
planner disqualified a custom asset channel.
Add the missing AutoReason enum value and handle ReasonCustomChannelData.
Wire static-address-backed loop-ins into the existing autoloop
planner and dispatch path. Loop-in rules can now be converted into
static candidates, prepared after global sorting, filtered with
static fee limits, and dispatched through the static manager.
This also fixes MaxAutoInFlight enforcement across all suggested
swap types and adds planner tests for missing static candidates
and mixed in-flight filtering.
Extend the public rpc surface for static autoloop integration
without turning the planner on yet. SuggestSwaps responses can
now carry static-address loop-in requests and the new planner
reason for missing static candidates is mapped over rpc.
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.
Add a dedicated loop-in source enum to the liquidity parameters
rpc and wire it through the internal parameter model and CLI.
This keeps the source selection explicit before any static
autoloop planning lands, so operators can choose between the
legacy wallet-funded path and a future static-address-backed
path without relying on implicit fallback behavior.
Disable JSON HTML escaping for recorded session files and for the nested
session payloads rewritten by bless mode. This keeps CLI text such as
"> 1 sat/vByte" readable instead of turning it into "\u003e".
Keep the metadata field order aligned with existing fixtures so a bless
pass does not rewrite unrelated sessions just because the encoder changed.