Skip to content

lncli unlock: wait until daemon can unlock#10536

Open
starius wants to merge 4 commits intolightningnetwork:masterfrom
starius:unlock-wait
Open

lncli unlock: wait until daemon can unlock#10536
starius wants to merge 4 commits intolightningnetwork:masterfrom
starius:unlock-wait

Conversation

@starius
Copy link
Collaborator

@starius starius commented Feb 2, 2026

Change Description

Use the StateService stream to wait for LOCKED before sending the unlock request, then wait for UNLOCKED/RPC_ACTIVE before reporting success. If the state shows the wallet is already unlocked, skip sending the unlock request and return an error immediately.

This avoids lost unlocks during slow startup. Fix #7749

Steps to Test

  • start LND
  • while it is still starting (ideally if compacting DB), run lncli unlock

In my case I see this:

lncli unlock
Input wallet password: 
wallet state: WAITING_TO_START
wallet state: LOCKED
wallet state: UNLOCKED

lnd successfully unlocked!

When LND is already unlocked, I see this:

lncli unlock
Input wallet password: 
wallet state: SERVER_ACTIVE
[lncli] wallet is already unlocked

Pull Request Checklist

Testing

  • Your PR passes all CI checks.
  • Tests covering the positive and negative (error paths) are included.
  • Bug fixes contain tests triggering the bug to prevent regressions.

Code Style and Documentation

📝 Please see our Contribution Guidelines for further guidance.

@gemini-code-assist
Copy link

Summary of Changes

Hello @starius, 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 robustness of the lncli unlock command. By integrating with LND's StateService, the command now intelligently waits for the wallet to be in an appropriate state before and after an unlock attempt. This prevents race conditions and improves user experience, especially during daemon startup or when the wallet is already active, ensuring reliable wallet management.

Highlights

  • Improved lncli unlock reliability: The lncli unlock command now proactively waits for the LND daemon's wallet to reach a LOCKED state before attempting an unlock, preventing premature unlock attempts.
  • Synchronized unlock process: After sending the unlock request, the command waits for the wallet to transition to UNLOCKED or RPC_ACTIVE before reporting success, ensuring the daemon is fully ready and responsive.
  • Enhanced error handling for invalid states: The command now explicitly checks for NON_EXISTING (wallet not initialized) or already UNLOCKED/RPC_ACTIVE/SERVER_ACTIVE states, providing immediate and informative feedback to the user.
  • Addresses slow startup issues: This change resolves the problem of 'lost unlocks' that could occur when LND is starting slowly, as reported in issue [feature]: ask password quickly after start #7749, by ensuring proper state synchronization.

🧠 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 Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@lightninglabs-deploy
Copy link
Collaborator

🟡 PR Severity: MEDIUM

CLI command implementation | 1 file | 104 lines changed

🟡 Medium (1 file)
  • cmd/commands/cmd_walletunlocker.go - CLI command implementation for wallet unlocking functionality

Analysis

This PR modifies the lncli unlock command implementation to add retry/wait logic. While wallet unlocking is security-sensitive functionality, this change is on the client-side CLI tool (cmd/commands/*), not the core server-side wallet unlocker service or RPC handler.

The change adds 104 lines to improve the user experience by waiting until the daemon is ready to accept unlock commands, rather than immediately failing. This is a focused, single-file change to CLI tooling.

Severity determination:

  • Not CRITICAL: Client-side tooling, not core wallet/channel/HTLC logic
  • Not HIGH: Not touching walletunlocker/* service or rpcserver.go
  • MEDIUM: CLI command implementation (falls under "Other Go files not categorized above")
  • No severity bumps apply (1 file, 104 lines, no critical packages)

Review focus: Ensure the retry logic handles edge cases properly (timeouts, connection failures, daemon state transitions).


To override, add a severity-override-{critical,high,medium,low} label.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses an important issue where lncli unlock could fail if run too quickly during lnd startup. The approach of using the StateService to wait for the correct wallet state is solid and well-implemented. The new functions are clear and well-commented.

However, there are a couple of areas for improvement:

  • Testing: The PR does not include any tests. Given that this change fixes a race condition and introduces new state-dependent logic, adding unit or integration tests would be crucial to prevent future regressions and ensure the fix is robust. The style guide also emphasizes testing.
  • Error Handling: The error handling for the stream closing prematurely could be more user-friendly.

Overall, this is a good fix that improves the robustness of lncli unlock.

@lightninglabs-deploy
Copy link
Collaborator

🟠 PR Severity: HIGH

Classification based on file analysis | 3 files | 665 lines changed

🟠 High (1 file)
  • cmd/commands/cmd_walletunlocker.go - Wallet unlocker CLI command (auth/security component)
🟢 Low (1 file)
  • docs/release-notes/release-notes-0.20.1.md - Release notes documentation
🧪 Test Files (excluded from severity)
  • cmd/commands/cmd_walletunlocker_test.go - Test file (522 lines)

Analysis

This PR modifies the lncli unlock command to wait until the daemon is ready to accept unlock requests. The main change is in the wallet unlocker CLI component, which falls under the HIGH severity category as it relates to authentication and wallet security.

The PR is classified as HIGH because:

  • It touches cmd/commands/cmd_walletunlocker.go, which is part of the wallet unlocking mechanism (auth/security)
  • The change modifies how the CLI interacts with the wallet unlock process, which is security-sensitive
  • No severity bump applied (only 2 non-test files, 143 non-test lines changed)

While this is a CLI-side change (not core wallet logic), it still warrants review by an engineer knowledgeable about the wallet unlocking workflow to ensure the retry logic and error handling are correct.


To override, add a severity-override-{critical,high,medium,low} label.

@starius
Copy link
Collaborator Author

starius commented Feb 2, 2026

  • Testing: The PR does not include any tests. Given that this change fixes a race condition and introduces new state-dependent logic, adding unit or integration tests would be crucial to prevent future regressions and ensure the fix is robust. The style guide also emphasizes testing.

I added unit tests in a separate commit.

  • Error Handling: The error handling for the stream closing prematurely could be more user-friendly.

Added handling of io.EOF.

@lightninglabs-deploy
Copy link
Collaborator

🟠 PR Severity: HIGH

Automated classification | 3 files | 666 lines changed

🟠 High (1 file)
  • cmd/commands/cmd_walletunlocker.go - Wallet unlock command implementation (auth/security component)
🟢 Low (2 files)
  • cmd/commands/cmd_walletunlocker_test.go - Test file
  • docs/release-notes/release-notes-0.20.1.md - Documentation

Analysis

This PR modifies the wallet unlock CLI command to wait until the daemon is ready to accept unlock requests. The changes touch authentication/security-related code in the walletunlocker command implementation, which falls into the HIGH severity category requiring a knowledgeable engineer to review.

The PR adds retry logic and proper timing handling to the unlock command, which is critical for wallet security operations. While the majority of additions are in test files (522 lines), the core implementation changes (131 lines in cmd_walletunlocker.go) warrant careful review to ensure the unlock flow works correctly and doesn't introduce timing vulnerabilities or race conditions.

Key concerns:

  • Proper handling of wallet unlock timing and retry logic
  • Authentication flow integrity
  • Error handling during daemon startup

To override, add a `severity-override-{critical,high,medium,low}` label.

@starius starius force-pushed the unlock-wait branch 2 times, most recently from 41cd873 to 2c8b2ee Compare February 3, 2026 01:33
@lightninglabs-deploy
Copy link
Collaborator

🟠 PR Severity: HIGH

lncli unlock: wait until daemon can unlock | 4 files | 808 lines changed

🟠 High (1 file)
  • cmd/commands/cmd_walletunlocker.go - Wallet unlocking CLI command (walletunlocker/* - Auth/security)
🟢 Low (2 files)
  • docs/release-notes/release-notes-0.20.1.md - Documentation
  • docs/release-notes/release-notes-0.21.0.md - Documentation
🧪 Test Files (1 file, excluded from classification)
  • cmd/commands/cmd_walletunlocker_test.go - Test coverage (620 lines)

Analysis

This PR enhances the lncli unlock command to wait until the daemon is ready to accept unlock requests, improving the user experience when unlocking the wallet. The core change is in the wallet unlocking CLI command implementation, which falls under authentication/security concerns.

Severity determination:

  • The primary changed file is cmd/commands/cmd_walletunlocker.go (185 net lines), which is categorized as HIGH severity per the walletunlocker/* auth/security classification
  • Effective file count: 1 file (excluding tests and docs)
  • Effective line count: 185 lines (excluding tests and docs)
  • No severity bump applied (thresholds: >20 files or >500 lines)

Review considerations:

  • Changes to wallet unlocking flow could affect authentication security
  • Good test coverage added (620 lines of new tests)
  • Documentation properly updated

To override, add a severity-override-{critical,high,medium,low} label.

@starius
Copy link
Collaborator Author

starius commented Feb 3, 2026

Rebased

@lightninglabs-deploy
Copy link
Collaborator

🟡 PR Severity: MEDIUM

CLI command changes | 3 non-test files | 188 lines changed

🟡 Medium (1 file)
  • cmd/commands/cmd_walletunlocker.go - CLI wallet unlock command implementation
🟢 Low (2 files)
  • docs/release-notes/release-notes-0.20.1.md - Release notes documentation
  • docs/release-notes/release-notes-0.21.0.md - Release notes documentation
📋 Test Files (1 file)
  • cmd/commands/cmd_walletunlocker_test.go - Test coverage

Analysis

This PR modifies the wallet unlock CLI command to wait until the daemon is ready to accept unlock requests. The changes are primarily in the CLI command layer (cmd/commands/), which falls into the MEDIUM severity category requiring focused review.

The PR adds retry logic and connection handling to the lncli unlock command, which improves user experience but doesn't touch critical wallet operations, HTLC logic, or consensus-critical code paths. The bulk of additions (620 lines) are test coverage, which is excluded from severity calculations.

No severity bump criteria met:

  • Non-test files: 3 (threshold: >20)
  • Non-test lines changed: 188 (threshold: >500)
  • Only one distinct package affected

To override, add a severity-override-{critical,high,medium,low} label.

Copy link
Collaborator

@ziggie1984 ziggie1984 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test cases wait_locked_non_existing (lines 365-378) and wait_unlocked_non_existing (lines 438-453) appear to be testing the exact same code path.

Both have only one state stream returning NON_EXISTING, which means the error occurs during the first "wait for locked" phase, before any unlock is attempted. The expectUnlockCalls: 0 and expectSubscribeCalls: 1 confirm this.

To actually test "wait_unlocked_non_existing" (receiving NON_EXISTING after the unlock call), it would need two streams:

{
    name:            "wait_unlocked_non_existing",
    readPasswordRet: []byte("pw"),
    stateStreams: []stateStreamSpec{
        {
            states: []lnrpc.WalletState{
                lnrpc.WalletState_LOCKED,  // First wait succeeds
            },
        },
        {
            states: []lnrpc.WalletState{
                lnrpc.WalletState_NON_EXISTING,  // Second wait fails
            },
        },
    },
    expectUnlockCalls:    1,  // Unlock was called
    expectSubscribeCalls: 2,  // Two streams used
    // ...
}

This looks like it might be a copy-paste issue where the second stream was forgotten.

@lightninglabs-deploy
Copy link
Collaborator

🟠 PR Severity: HIGH

Wallet unlock flow changes | 1 file | 185 lines changed

🟠 High (1 file)
  • cmd/commands/cmd_walletunlocker.go - Modifies wallet unlock logic to use StateService stream, affecting authentication/security flow
🟢 Test/Documentation (3 files)
  • cmd/commands/cmd_walletunlocker_test.go - Test coverage (661 lines)
  • docs/release-notes/release-notes-0.20.1.md - Release notes
  • docs/release-notes/release-notes-0.21.0.md - Release notes

Analysis

This PR addresses issue #7749 where wallet unlock requests could be lost during slow LND startup. The change modifies the wallet unlock command flow to:

  1. Wait for the LOCKED state before sending unlock request
  2. Use StateService stream to track wallet state transitions
  3. Avoid race conditions during database compaction or slow startup

Severity Rationale:

  • HIGH severity is appropriate because:
    • The walletunlocker component is part of the authentication/security system
    • Changes affect the critical wallet unlock flow
    • Improper handling could lead to locked funds or startup issues
    • Requires review from engineers knowledgeable about LND's startup sequence and state management

Key Review Points:

  • StateService stream subscription and error handling
  • Race condition mitigation during slow startup
  • Behavior when wallet is already unlocked
  • Error path coverage in the new test file

To override, add a severity-override-{critical,high,medium,low} label.

@starius
Copy link
Collaborator Author

starius commented Feb 4, 2026

The test cases wait_locked_non_existing (lines 365-378) and wait_unlocked_non_existing (lines 438-453) appear to be testing the exact same code path.

Both have only one state stream returning NON_EXISTING, which means the error occurs during the first "wait for locked" phase, before any unlock is attempted. The expectUnlockCalls: 0 and expectSubscribeCalls: 1 confirm this.

[...]

This looks like it might be a copy-paste issue where the second stream was forgotten.

Great catch! I fixed "wait_locked_non_existing" test case and also added comments before test cases to explain what happens in each test case.

@ziggie1984
Copy link
Collaborator

needs a rebase

Use the StateService stream to wait for LOCKED before sending the unlock
request, then wait for UNLOCKED/RPC_ACTIVE before reporting success.
If the state shows the wallet is already unlocked, skip sending the
unlock request and return an error immediately.

This avoids lost unlocks during slow startup.
Fix lightningnetwork#7749
Add table-driven unit tests for unlock() that exercise success and error paths,
cover flag and arg handling.
@starius
Copy link
Collaborator Author

starius commented Feb 4, 2026

Rebased

@lightninglabs-deploy
Copy link
Collaborator

🟠 PR Severity: HIGH

lncli unlock improvements | 3 files (excl. tests) | 188 lines changed

🟠 High (1 file)
  • cmd/commands/cmd_walletunlocker.go - Wallet unlocking command logic and authentication flow
🟢 Low (2 files)
  • docs/release-notes/release-notes-0.20.1.md - Release notes update
  • docs/release-notes/release-notes-0.21.0.md - Release notes update
ℹ️ Excluded from severity calculation
  • cmd/commands/cmd_walletunlocker_test.go - Test file (661 lines)

Analysis

This PR modifies the wallet unlocking command logic in lncli, which falls under the walletunlocker/* category classified as HIGH severity due to its role in authentication and security. The changes implement a retry mechanism to wait until the daemon is ready to unlock, which affects the wallet unlocking workflow.

While the majority of additions are test code (661 lines), the core logic change in cmd_walletunlocker.go (181 lines) is substantial and touches authentication/security functionality. This requires review by an engineer knowledgeable in lnd's wallet unlocking flow and CLI command behavior.

The PR does not meet the criteria for a severity bump (not >20 files, not >500 non-test lines, not multiple critical packages).

Recommendation: Review should focus on:

  • Retry logic and timing behavior
  • Error handling during unlock attempts
  • Impact on wallet unlocking UX and security

To override, add a severity-override-{critical,high,medium,low} label.

Copy link
Collaborator

@ziggie1984 ziggie1984 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature]: ask password quickly after start

3 participants