Repository navigation
Conversation
hieblmi
force-pushed
the
multi-address-core
branch
4 times, most recently
from
August 31, 2026 10:37
a1b5ec5 to
c74ac6c
Compare
hieblmi
force-pushed
the
multi-address-core
branch
6 times, most recently
from
September 3, 2026 06:16
ee1bbba to
5b4bd5b
Compare
starius
reviewed
Sep 3, 2026
hieblmi
force-pushed
the
multi-address-core
branch
from
September 3, 2026 08:08
5b4bd5b to
c49a43e
Compare
Collaborator
|
@starius: review reminder |
hieblmi
force-pushed
the
multi-address-core
branch
from
September 11, 2026 07:08
c49a43e to
56816b7
Compare
starius
reviewed
Sep 11, 2026
hieblmi
force-pushed
the
multi-address-core
branch
2 times, most recently
from
September 17, 2026 08:14
2f594a8 to
205077e
Compare
Collaborator
|
@starius: review reminder |
hieblmi
force-pushed
the
multi-address-core
branch
4 times, most recently
from
September 28, 2026 14:40
ad14247 to
5b76079
Compare
Create a fresh static address for fractional loop-in change and send its descriptor to the server. Reconstruct signed HTLCs with the persisted parameters and verify cooperative batch change by output script.
Multi-address loop-ins sign and construct transactions from the parameters attached to each deposit and their dedicated change address. The legacy root address fields therefore became write-only, but populating them could still abort signing, sweep handling, or recovery when the root lookup failed. Remove those fields and lookups, select the FSM from the protocol version persisted with the swap, and set that version before constructing new state machines. Keep the root-parameter lookup used by autoloop expiry calculation and add regression coverage for recovery and unsupported persisted versions.
Create a fresh static address for partial-withdrawal change and identify it in the confirmed transaction through its active change-family script, without assuming output order or count. Record withdrawn and change amounts by script identity. Keep all withdrawal outputs in the PSBT without separate signing metadata while preserving full-withdrawal behavior.
Let loop static deposit create an address and print its lncli sendcoins command. Add wallet funding for new or existing owned addresses, validating arguments before issuance. Preserve addresses and error causes for ambiguous funding outcomes, including transport failures. Advise checking wallet transactions before retrying; cover both funding paths and update RPC and CLI docs.
Include the owning static address in every deposit RPC response and CLI listing. Users can distinguish deposits created by different receive and change addresses without reconstructing scripts externally. Calculate blocks until expiry from each deposit owner instead of the legacy root address, and reject deposits whose owning parameters are missing. Centralize deposit response conversion and update generated RPC artifacts, regression coverage, and command replay fixtures.
The CLI previously recognized an uninitialized static-address seed by searching arbitrary gRPC error text. Any wrapping or wording change could suppress the L402 backup warning before a user funded a newly derived address. Map ErrNoStaticAddress to codes.NotFound at the RPC boundary and classify that status in the CLI. Retain compatibility with older daemons only for an exact Unknown-status message, avoiding the broad substring match, and cover both sides with regression tests.
A static-address account can now receive deposits across multiple derived addresses, so the singular summary field can no longer describe the current receive address. Removing or repurposing field 1 would break existing clients. Keep the wire value as the legacy/root derivation address, formally deprecate it, document the expiry as the shared CSV delay, and direct CLI users to derive a fresh receive address. Rename the server locals to make the compatibility behavior explicit and regenerate protobuf and Swagger artifacts.
hieblmi
force-pushed
the
multi-address-core
branch
from
October 27, 2026 14:33
8400ee7 to
215ef7b
Compare
Keep pending deposits owned while historical HTLC confirmations recover. Release only the calling swap's claims, after an unspent original or confirmed conflict rules out a confirmed HTLC. Retry temporary wallet failures and finish old swaps after their deposits are reused. Claim ownership before initialization. Before startup releases a stale legacy mapping, recover its unique pending or paid owner and fail closed on ambiguous claims. Preserve current mappings during valid retries. Persist unconfirmed funding parents before signing so confirmed wallet replacements can prove a conflict after restart. Missing history or an absent legacy snapshot never proves the old HTLC cannot confirm. Retain replacement expiry-sweep tracking and regression coverage for crashes, funding replacement, reuse and delayed notifications.
Cover per-deposit address ownership and operation-specific change outputs across the shared SQL persistence boundary. Reconstruct the deposit, loop-in, and withdrawal stores to verify ownership and change metadata survive restart.
The sweep request handler only compared the number of prevouts the server sent against the number of sweep inputs. A list of the right length could still contain duplicate outpoints or reference outpoints the sweep doesn't spend. The prevout fetcher then returns nil for an input and NewTxSigHashes panics on the nil dereference, taking down loopd on a malformed server request. Reject duplicate prevouts while building the prevout map and require a prevout for every sweep input before computing sighashes.
Use each selected deposit's address parameters when checking quote expiry. Also check the actual server HTLC expiry against every deposit's CSV expiry and safety buffer before sharing signatures. Reject unsafe contracts, abandon their server attempt, and cancel their invoice.
Rename the legacy key family and address parameter alias, reconcile root terminology, document script map keys and consolidate wallet UTXO listing. Keep key-family values and underlying script parameters unchanged. Share address decoding and script reconstruction, convert identical SQL rows directly, and use per-deposit expiry for autoloop. Clarify method and test invariants, include scripts in missing-parameter errors, preallocate store results and update CLI and RPC documentation.
Keep address creation separate from wallet funding. Require wallet:fund for the funding RPC. Keep static deposit as a CLI helper that creates an address and prints an lncli sendcoins command for manual funding. Report funding timeouts and cancellations as unknown outcomes, retaining the address and advising callers to inspect wallet transactions first.
Require one legacy address when deposits exist before migration 24 assigns ownership. Use a regular CHECK-constrained guard table on both backends without an additional PostgreSQL TEMPORARY privilege. Cover backfills, ambiguous ownership, restricted roles, rollback and retry. Document recovery only for a verified rolled-back dirty migration 24, retaining the migration framework's existing dirty-version behavior.
Encode each persisted P2TR script using an explicit network in a free helper. Address rendering does not need the manager or reconstruction of the signing keys and script tree, and does not imply an ownership check.
Quote selected deposits with one exact active-set lookup, retaining presence, state, duplicate and expiry checks without rendering history. Refresh deposits before taking the unspent response snapshot and filter against active Deposited records. Historical database rows cannot revive outputs removed during reconciliation or return stale confirmations.
Keep the lowest-ID active address alongside the script index. Publish it only after successful activation and update it under the index mutex. Serve public root lookups from the cache with independent parameter, script and public-key copies. Cover restart recovery, failed imports and caller mutation so caching preserves readiness and value ownership.
Read wallet UTXOs outside the map lock, then match their scripts directly against the active address index under a short lock. This removes the full-index allocation and copy on each poll while preserving confirmation bounds and allowing address activation during the wallet RPC. Test filtering across addresses and verify wallet RPCs do not hold the map lock.
Load persisted addresses in bounded pages using an ascending ID cursor during startup and root recovery. Read wallet watches once and publish the complete runtime index only after every page and import succeeds. The existing bulk-read API remains available for compatibility. Test sparse IDs, page boundaries, failed-page recovery and restart behavior, including the SQL query on SQLite and PostgreSQL.
Replace the issuance mutex with a context-aware gate so queued callers can cancel without disturbing the current issuer. Keep root creation, recovery and derived issuance serialized and retryable. A private mutex now explicitly guards the active script index and root. Test canceled root and derived waiters, subsequent progress and concurrent first callers sharing one root.
Remove the blank line before the closing brace flagged by CI. This is formatting only; test behavior is unchanged.
Compare lnd counters with persisted address and HTLC indices, verify wallet identity and advance only lagging families before activation. Preserve equal or ahead counters without creating addresses. Allow restoration beyond the manager deadline with RPC timeouts, cancellation and progress logs. Finish listener initialization before shutdown teardown to avoid leaking REST or racing wait groups. Cover restoration, interrupted restarts and shutdown order.
A restored wallet may not have derived a static address key. Pubkey lookup then misses and can fall back to locator (0, 0), producing an invalid expiry signature. Pass the deposit locator and omit PubKey so lndclient sends the locator over the wire. Cover root, receive and change deposits.
Document fresh receive-address derivation, lazy seed initialization, funding-address lookup hardening, and the swap:execute permission required by address creation. Regenerate the CLI, gRPC, Swagger, and man-page documentation and add feature, breaking-change, and recovery release notes.
Cost polling can retain a fee share before discovery stores another static deposit from the same transaction. Different scripts caused the first deposit to receive the entire fee permanently. Recognize every owned static output through the active address index before allocating the fee. Preserve value-based shares, remainder placement, and existing change and unknown-fee behavior.
Recognize legacy root change alongside derived change and preserve accounting when both outputs pay the root. Reuse the persisted change address across fee bumps and restarts while retaining old transactions and creating fresh signing sessions. Compute withdrawal values once. Cover legacy and derived change, root destinations, repeated fee bumps, full withdrawals and unrelated outputs.
Legacy deposit ownership mappings can omit inputs from a partial swap. Refuse cooperative sweep signing when its original deposit snapshot is incomplete, so missing inputs cannot hide change owed to the client. Validate the requested swap as well as swaps discovered by the batched input lookup. Cover complete, partial, and unmapped legacy joins with real store regressions.
hieblmi
force-pushed
the
multi-address-core
branch
from
October 27, 2026 15:54
215ef7b to
226f0b8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is PR 1 of 3 in the Static Address multi-address stack.
Static Address previously treated one legacy/root address as the owner of every
deposit. This PR introduces fresh receive and operation-specific change
addresses while preserving the address parameters that own each deposit.
Loop-ins and withdrawals can consequently spend deposits received across
multiple derived addresses, with each input signed and proven using its actual
address parameters.
Key Changes
across restarts.
address.
loop-ins and partial withdrawals.
errors for the expected Taproot output key.
of reconstructing and scanning every persisted address.
paying the same destination script cannot finalize a deposit.
loop static depositas a helper that creates a fresh address andprints an
lncli sendcoinscommand for manual funding.RPC and Compatibility Changes
NewStaticAddressnow derives a fresh receive address on every request andnever funds it. Callers must not assume repeated requests are idempotent or
return the same address.
Wallet funding is handled by the separate
FundStaticAddressRPC, exposed asPOST /v1/staticaddr/fund. It creates and funds a fresh address whensend_coins_request.addris empty, or funds a known existing static addresswhen it is set. The CLI does not call this funding endpoint:
loop static depositcallsNewStaticAddressand prints anlncli sendcoinscommandinstead of broadcasting a transaction.
loop static newkeeps its address-onlyoutput.
Funding requires
wallet:fundin addition toswap:executeandloop:in,or an explicit URI grant for
FundStaticAddress. Existing execute/in andNewStaticAddressURI macaroons cannot authorize funding. The default localLoop macaroon is regenerated on startup when permissions change; distributed
or custom funding macaroons must be explicitly updated.
Because address creation mutates wallet and database state, the RPC permission
changes from
swap:readtoswap:execute. Operators using custom scopedmacaroons must rebake them accordingly.
StaticAddressSummaryResponse.static_addressremains populated with thelegacy/root address for wire compatibility, but is deprecated and must not be
treated as the current receive address. Call
NewStaticAddressto derive afresh address.
Database and Recovery
The database now records:
Migrations backfill existing records using the legacy/root address. Store
reconstruction restores deposit ownership and loop-in and withdrawal change
metadata after restart. Recovery ranks retry ownership by persisted swap
order, keeps ambiguous deposits locked without blocking startup, and retains
every known original input for pending monitors. Cooperative signing rejects
incomplete original-input snapshots rather than calculating change from a
partial historical join.
Stacked PRs
validates and follows the transactions that actually replace multi-address
withdrawals.
persists the confirmed HTLC output so recovery can rebuild the exact
server-published transaction.
The address startup and lookup hardening previously isolated in
#1214 has been folded into
this PR, and #1214 is now closed.
Testing
Multi-address integration tests are in
lightninglabs/nautilus#1660,
at
37163f3a.Its Loop client,
looprpc, andswapserverrpcmodules all pin this PR'shead
226f0b8e.Full integration CI run.
Final local validation:
CLI, root package, sweepbatcher, and utils.
withdrawal RBF, and fractional multi-address loop-in with change.
git diff --check.Release Notes
Release notes document the new address behavior, RPC permission change,
deprecated summary field, migration compatibility, and address lookup
hardening.