@@ -70,8 +70,8 @@ matching wallet child and recreate the one concrete static-address row.
7070Current V0 backups initialize ` legacy_first_height ` from the legacy concrete
7171static-address initiation height and ` multi_address_first_height ` from the
7272current block height when the backup is written. They are separate fields so
73- the future multi-address scan floor is independent from the legacy concrete
74- address import hint.
73+ the multi-address scan floor is independent of the legacy concrete address
74+ import hint.
7575
7676The Taproot address string, ` pkScript ` , and scan lookahead/gap limit are not
7777backed up. The address and ` pkScript ` can be derived from the stored key and
@@ -88,15 +88,16 @@ are not replayed from the backup.
8888
8989## Why Root And Legacy Fields Are Both Stored
9090
91- The server pubkey, protocol version, expiry, planned multi-address
92- receive/change key families, Bitcoin network, and multi-address first height
93- define the stable fields future restore code will combine with lnd-derived
94- client keys and a chain scan. They are not used by the current V0 restore path .
91+ The server pubkey, protocol version, expiry, multi-address receive/change key
92+ families, Bitcoin network, and multi-address first height are stable for the
93+ L402 generation. Multi-address restore combines those fields with lnd-derived
94+ client keys and matches the resulting scripts against wallet-visible UTXOs .
9595
96- The current V0 restore path recreates the existing concrete address row
97- directly, so the backup also stores that row's client pubkey, legacy client key
98- family, and legacy first height. Those fields let restore find the matching
99- local wallet child and import the concrete address from the right chain height.
96+ The legacy concrete address predates the receive/change branches and is restored
97+ as one explicit row. The backup therefore also stores that row's client pubkey,
98+ legacy client key family, and legacy first height, which let restore find the
99+ matching local wallet child and import the concrete address from the right
100+ height.
100101
101102## Encryption Model
102103
@@ -196,22 +197,25 @@ Restore performs the following steps:
196197 existing file has identical contents
1971987 . import the tapscript into lnd and create or reuse the local concrete
198199 static-address record
199- 8 . if static-address restore fails after token files were written, remove only
200+ 8 . scan the multi-address receive/change branches against wallet-visible UTXOs
201+ and recreate matching concrete address rows
202+ 9 . if an address restore phase fails after token files were written, remove only
200203 the token files written by this restore attempt
201- 9 . trigger best-effort deposit reconciliation
204+ 10 . trigger best-effort deposit reconciliation
202205
203206Client-key reconstruction uses the following strategy:
204207
205208- scan child indexes ` 0 ` through ` 20 ` in the legacy static-address client key
206209 family using ` DeriveKey `
207210- accept the child whose derived pubkey matches the backed-up client pubkey
208211
209- The multi-address scan-and-rebuild flow is not active yet. The immutable backup
210- already contains the address-space metadata that flow will need.
212+ Multi-address scanning uses the immutable address-space fields in the backup to
213+ derive receive/change candidates without requiring a backed-up last-issued child
214+ index.
211215
212- ## Future Multi-Address Generation
216+ ## Multi-Address Generation
213217
214- The planned multi-address model uses two dedicated client-side key families:
218+ The multi-address model uses two dedicated client-side key families:
215219
216220- ` swap.StaticMultiAddressKeyFamily ` for externally visible static-address
217221 deposits
@@ -221,8 +225,8 @@ The planned multi-address model uses two dedicated client-side key families:
221225The legacy ` swap.StaticAddressKeyFamily ` remains the V0 concrete static-address
222226family and the static-address HTLC key family.
223227
224- The future ` static_addresses ` table remains a table of concrete derived
225- addresses. Each row represents one address child and stores:
228+ The ` static_addresses ` table remains a table of concrete derived addresses.
229+ Each row represents one address child and stores:
226230
227231- the client pubkey
228232- the server pubkey
@@ -235,7 +239,7 @@ addresses. Each row represents one address child and stores:
235239The immutable backup does not store every row. Instead it stores the
236240address-space metadata that allows those rows to be rediscovered by scanning.
237241
238- For each future receive or change address:
242+ For each receive or change address:
239243
2402441 . the client chooses the appropriate key family
2412452 . the client derives the next pubkey from lnd for that family
@@ -248,12 +252,19 @@ For each future receive or change address:
248252The client key used in the MuSig2 aggregate key should also be the client's key
249253in the timeout path for that concrete multi-address output.
250254
251- Because the backup is immutable, future restore must regenerate candidate
252- receive and change children from the backed-up key families and the restored
253- lnd key material, rescan from the backed-up multi-address first height, and
254- rebuild local table rows from what is found on chain. The lookahead/gap limit
255- used during that scan is a restore parameter, not immutable backup data. Restore
256- must not depend on a mutable "last issued child index" snapshot.
255+ Because the backup is immutable, restore regenerates candidate receive and
256+ change children from the backed-up branch fields and matches their scripts
257+ against lnd's wallet-visible UTXOs. The scan uses a rolling gap limit: each
258+ matched child resets the unused counter, and restore stops only after a full
259+ gap of consecutive unused children. The default gap is restore policy, not
260+ immutable backup data, so restore does not depend on a mutable "last issued
261+ child index" snapshot.
262+
263+ The multi-address branch scan takes one unfiltered ` ListUnspent ` snapshot from
264+ lnd and matches all derived candidates against that in-memory script set.
265+ Deposit reconciliation runs after matching and performs its own ` ListUnspent `
266+ pass through the address manager so it can use the newly active address rows and
267+ confirmation metadata.
257268
258269## Server Proof For Multi-Address Inputs
259270
@@ -286,18 +297,19 @@ and chain-scan problem.
286297
287298## Operational Limits
288299
289- Restore in this implementation recreates the V0 one-address model only.
300+ Restore in this implementation rebuilds current wallet-visible static-address
301+ state, not the full historical database.
290302
291303Some practical consequences follow from that:
292304
293305- restoring an older immutable backup is best done into a fresh Loop data
294- directory, or into a directory that already contains the same token and
295- static- address row
296- - only one concrete static address can be recreated directly by this restore
297- code
298- - conflicting local ` l402.token ` contents or a different existing
299- static-address row cause restore to fail rather than overwrite local state
300- - active deposits are rebuilt best-effort from wallet reconciliation, not by
306+ directory
307+ - the legacy concrete static address is restored directly
308+ - multi- address receive/change rows are recreated for wallet-visible unspent
309+ outputs discovered by the rolling branch scan
310+ - conflicting local ` l402.token ` contents or different existing address rows
311+ cause restore to fail rather than overwrite local state
312+ - historical deposit state is rebuilt best-effort from reconciliation, not by
301313 replaying every stored deposit transition
302314
303315## Why The Backup Is Immutable
@@ -324,13 +336,13 @@ This package owns:
324336- immutable backup-file discovery and selection
325337- paid L402 token-file backup and restore
326338- V0 static-address key re-derivation and restore orchestration
327- - static-address metadata fields for future multi-address restore
339+ - static-address root fields for multi-address restore
340+ - multi-address branch scanning against wallet-visible UTXOs
328341- post-restore deposit reconciliation orchestration
329342
330343This package does not own:
331344
332345- CLI command handling
333346- gRPC transport
334347- the static-address server protocol
335- - the future multi-address scanning implementation
336348- ` loopd ` startup wiring
0 commit comments