Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e92462e
staticaddr: track unconfirmed deposits
hieblmi Apr 27, 2026
1da7419
staticaddr/deposit: replay startup block to recovered deposits
hieblmi Apr 29, 2026
9d0a196
staticaddr: apply confirmation policy by flow
hieblmi Apr 27, 2026
c1295bb
cmd/loop: warn for auto-selected low-conf deposits
hieblmi Apr 27, 2026
ce8b835
staticaddr/loopin: cancel orphan invoice when init fails early
hieblmi Mar 24, 2026
a12127f
staticaddr/deposit: async finalization cleanup
hieblmi Apr 20, 2026
ac76882
staticaddr: cancel loop-ins when deposit inputs vanish
hieblmi Apr 22, 2026
80bc65d
staticaddr: harden client deposit readiness
hieblmi Apr 27, 2026
44dd32e
staticaddr/loopin: wait for risk acceptance notification
hieblmi Apr 27, 2026
a235126
staticaddr/loopin: handle risk rejection notification
hieblmi Apr 27, 2026
8128304
staticaddr/loopin: list failed swaps by state
hieblmi Apr 28, 2026
cca151c
recover L402 and static address
hieblmi May 5, 2026
2890bcb
staticaddr: persist multi-address deposits
hieblmi May 6, 2026
b6292f4
staticaddr: sign deposits with their own address params
hieblmi May 6, 2026
3789ebd
swapserverrpc: add static address input proofs
hieblmi May 6, 2026
ba47286
staticaddr: send client keys for deposit inputs
hieblmi May 6, 2026
ff88a93
staticaddr/loopin: restore deposit address params
hieblmi May 6, 2026
9e656ff
staticaddr: use generated change addresses
hieblmi May 6, 2026
7f06bed
recovery: restore multi-address static deposits
hieblmi May 7, 2026
e211c2d
staticaddr: fund new address with sendcoins
hieblmi May 8, 2026
fc65200
staticaddr: include address in deposit list
hieblmi May 11, 2026
aadf2e8
loop: add manual static deposit recovery
hieblmi May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,28 @@ To execute a Loop In:
loop in <amt_in_satoshis>
```

### Static Address Recovery
Loop now keeps at most one encrypted immutable recovery backup per paid L402
generation in the active network data directory. A backup is written only after
Loop has both the paid `l402.token` and the concrete static-address parameters
for that generation.

The backup is encrypted with a key derived by the backing `lnd` wallet. It is
therefore only useful with that same `lnd` instance, or with an `lnd` restored
from the same seed/key material. The Loop backup file alone is not enough to
recover static-address access.

Existing static-address users get this backup backfilled on the next startup
with the upgraded client. A fresh install that has no local L402 or
static-address state first checks for an existing recovery backup in the active
network directory. If none is restored, startup materializes the initial
paid-L402/static-address generation and writes its backup.

The follow-up multi-address work is expected to keep this one-backup-per-L402
model and use the deterministic receive/change key-family metadata already
stored in the backup. See [recovery/README.md](./recovery/README.md) for the
full recovery model and the planned multi-address outlook.

### More info

- [Loop FAQs](./docs/faqs.md)
Expand Down
4 changes: 3 additions & 1 deletion cmd/loop/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ var (
monitorCommand, quoteCommand, listAuthCommand, fetchL402Command,
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand,
getInfoCommand, abandonSwapCommand, reservationsCommands,
getInfoCommand, abandonSwapCommand, recoverCommand,
recoverDepositCommand,
reservationsCommands,
instantOutCommand, listInstantOutsCommand, stopCommand,
printManCommand, printMarkdownCommand,
}
Expand Down
114 changes: 114 additions & 0 deletions cmd/loop/recover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package main

import (
"context"

"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli/v3"
)

var recoverCommand = &cli.Command{
Name: "recover",
Usage: "restore static address and L402 state from a local backup file",
Description: "Restores the local static-address state and L402 token " +
"from an encrypted backup file. If --backup_file is omitted, " +
"loopd selects the latest decryptable active-network backup " +
"candidate and fully validates it before restoring state.",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "backup_file",
Usage: "path to an encrypted backup file; if omitted, " +
"loopd selects and validates the latest active-network " +
"backup candidate",
},
},
Action: runRecover,
}

var recoverDepositCommand = &cli.Command{
Name: "recoverdeposit",
Usage: "recover one static address deposit from on-chain data",
Description: "Verifies the provided transaction output on-chain, " +
"restores the matching static address, stores the deposit, and " +
"starts normal deposit tracking.",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "txid",
Usage: "transaction ID containing the deposit output",
Required: true,
},
&cli.UintFlag{
Name: "vout",
Usage: "deposit output index",
Required: true,
},
&cli.IntFlag{
Name: "height_hint",
Usage: "block height hint for the deposit transaction",
Required: true,
},
&cli.StringFlag{
Name: "pkscript_hex",
Usage: "expected static address P2TR pkScript in hex",
Required: true,
},
&cli.UintFlag{
Name: "scan_limit",
Usage: "optional highest child index to scan in each " +
"static address key family",
},
},
Action: runRecoverDeposit,
}

func runRecover(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() > 0 {
return showCommandHelp(ctx, cmd)
}

client, cleanup, err := getClient(cmd)
if err != nil {
return err
}
defer cleanup()

resp, err := client.Recover(
ctx, &looprpc.RecoverRequest{
BackupFile: cmd.String("backup_file"),
},
)
if err != nil {
return err
}

printRespJSON(resp)
return nil
}

func runRecoverDeposit(ctx context.Context, cmd *cli.Command) error {
if cmd.NArg() > 0 {
return showCommandHelp(ctx, cmd)
}

client, cleanup, err := getClient(cmd)
if err != nil {
return err
}
defer cleanup()

resp, err := client.RecoverDeposit(
ctx, &looprpc.RecoverDepositRequest{
Txid: cmd.String("txid"),
Vout: uint32(cmd.Uint("vout")),
HeightHint: int32(cmd.Int("height_hint")),
PkscriptHex: cmd.String("pkscript_hex"),
ScanLimit: uint32(cmd.Uint("scan_limit")),
},
)
if err != nil {
return err
}

printRespJSON(resp)
return nil
}
Loading
Loading