recovery: L402 and static address recovery from local backup file#1121
recovery: L402 and static address recovery from local backup file#1121hieblmi wants to merge 6 commits intolightninglabs:masterfrom
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the resilience of Loop by introducing a robust local recovery system. It allows users to restore their static address and L402 client state from an encrypted backup, ensuring continuity of operations even after data loss or a fresh installation. The changes include automated backup creation on startup, a new CLI command for restoration, and a dedicated recovery service that handles encryption, key derivation, and integration with existing static address and deposit management functionalities. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a local recovery mechanism for Loop static addresses and L402 authentication state. It includes a new recovery package for managing encrypted backups, a recover CLI command, and a gRPC service to trigger the restoration process. On the daemon side, backups are automatically generated during startup. Feedback suggests that backup failures during startup should not prevent the daemon from running and recommends enhancing the atomic file writing logic with explicit synchronization and better temporary file cleanup.
loopd/daemon.go
Outdated
| backupFile, err := recoveryService.WriteBackup(d.mainCtx) | ||
| if err != nil { | ||
| return fmt.Errorf("unable to write backup file: %w", err) | ||
| } | ||
| if backupFile != "" { | ||
| infof("Wrote encrypted backup file to %s", backupFile) | ||
| } |
There was a problem hiding this comment.
Failing to write a backup file on startup should probably not prevent the entire daemon from starting. While backups are important, a failure here (e.g., due to temporary disk issues or permission problems) shouldn't cause a regression in the availability of the swap service. Consider logging the error and continuing instead of returning it.
recoveryService := recovery.NewService(
d.cfg.DataDir, d.cfg.Network, d.lnd.Signer, d.lnd.WalletKit,
staticAddressManager, depositManager,
)
backupFile, err := recoveryService.WriteBackup(d.mainCtx)
if err != nil {
errorf("Unable to write backup file: %v", err)
} else if backupFile != "" {
infof("Wrote encrypted backup file to %s", backupFile)
}
recovery/service.go
Outdated
| func writeFileAtomically(path string, data []byte, mode os.FileMode) error { | ||
| tempPath := path + ".tmp" | ||
|
|
||
| err := os.WriteFile(tempPath, data, mode) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return os.Rename(tempPath, path) | ||
| } |
There was a problem hiding this comment.
To ensure the durability of the backup file, it is recommended to Sync() the file before closing and renaming it. Additionally, using defer os.Remove(tempPath) ensures that the temporary file is cleaned up if the rename operation fails.
func writeFileAtomically(path string, data []byte, mode os.FileMode) error {
tempPath := path + ".tmp"
f, err := os.OpenFile(tempPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
if err != nil {
return err
}
defer func() {
f.Close()
_ = os.Remove(tempPath)
}()
if _, err := f.Write(data); err != nil {
return err
}
if err := f.Sync(); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
return os.Rename(tempPath, path)
}bcf3b89 to
17804b9
Compare
17804b9 to
a2e5f02
Compare
Introduce a dedicated recovery package for encrypted local backups of static-address state and raw l402 token files. The package owns the backup file format, seed-derived encryption, static-address key re-derivation with gap fallback, token file restore, and best-effort deposit reconciliation orchestration. It also includes package-level documentation and focused tests for the file helpers.
Grant the manual recovery RPC the auth-write and static-address loop-in permissions it needs so loopd can restore local L402 material and static-address state through the authenticated client connection.
Register a recovery gRPC endpoint, attach the new recovery package to the swap client server, and have daemon startup write an encrypted backup file whenever recoverable static-address or l402 state already exists locally.
Expose the new recovery flow through loop-cli with a dedicated recover command that calls loopd over the existing RPC connection and optionally accepts a custom backup file path.
a2e5f02 to
83e2547
Compare
This PR adds phase-1 local recovery for static-address and L402 client state.
When loopd starts and finds an existing static address or local l402.token* state, it now writes an encrypted backup file into the active Loop data
directory. A fresh client can later restore from that file with loop recover --backup_file , or by omitting the flag and using the default
backup path.
What Changed
Backup Contents
The encrypted backup stores:
The L402 state is backed up as raw file blobs rather than decomposed token fields so restore stays compatible with Aperture’s current file-store
format.
Restore Flow
loop recover now restores the local client state by:
Encryption
The backup file is encrypted with a deterministic key derived from lnd using Signer.DeriveSharedKey, so the backup remains bound to the same
underlying seed material without requiring an interactive password in phase 1.
Implementation Notes
deposit reconciliation flow for best-effort recovery.