feat(lite): startup unlock prompt + real-backend encryption verification

Startup lock screen (soft): once the first refresh reveals the auto-opened wallet
is encrypted+locked, show the unlock modal on launch (reusing renderLiteUnlockPrompt,
one-shot per session). Soft by design — balances stay viewable via viewing keys
while locked, so the user may dismiss and browse read-only; only spending needs
the passphrase.

Real-backend verification: add `lite_smoke --encrypt` (create -> encryptionstatus
-> encrypt -> lock -> unlock, checking flags; passphrase never printed). Running it
against the real SDXL backend showed encrypt LOCKS immediately
(after encrypt: encrypted=1, locked=1) — the backend removes spending keys right
after encrypting. The controller already relays encryptionstatus faithfully (UI is
state-driven, so unaffected), but the fake modeled encrypt->unlocked; corrected the
fake (encrypt -> encrypted+locked) and the test sequence (encrypt -> unlock -> lock
-> decrypt) to match real behavior.

Builds clean, tests pass, hygiene clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 18:53:35 -05:00
parent d52d3d1b7f
commit 950d7ace50
5 changed files with 68 additions and 13 deletions

View File

@@ -128,10 +128,11 @@ inline char* liteFakeExecute(const char* command, const char* args)
++g_liteFakeSaveCount;
return liteFakeDup("{\"result\":\"success\"}");
}
// Encryption state machine. encrypt -> encrypted (keys stay in memory this session);
// lock/unlock toggle locked; decrypt clears it; encryptionstatus reports {encrypted,locked}.
// Encryption state machine. encrypt -> encrypted AND locked (matches the real backend,
// which removes keys from memory right after encrypting — verified via lite_smoke --encrypt);
// unlock/lock toggle locked; decrypt clears it; encryptionstatus reports {encrypted,locked}.
if (std::strcmp(c, "encrypt") == 0) {
g_liteFakeEncrypted = true; g_liteFakeLocked = false;
g_liteFakeEncrypted = true; g_liteFakeLocked = true;
return liteFakeDup("{\"result\":\"success\"}");
}
if (std::strcmp(c, "unlock") == 0) {

View File

@@ -4777,7 +4777,8 @@ void testLiteWalletControllerEncryption()
return c;
};
// encrypt -> lock -> unlock -> decrypt, observed via encryptionStatus()
// encrypt -> unlock -> lock -> decrypt, observed via encryptionStatus(). NOTE: encrypt LOCKS
// immediately (the real backend removes keys after encrypting — verified via lite_smoke).
{
auto c = open();
const auto s0 = c->encryptionStatus();
@@ -4788,17 +4789,17 @@ void testLiteWalletControllerEncryption()
EXPECT_TRUE(c->encryptWallet("walletpass").ok);
const auto s1 = c->encryptionStatus();
EXPECT_TRUE(s1.encrypted);
EXPECT_FALSE(s1.locked);
EXPECT_TRUE(c->lockWallet());
const auto s2 = c->encryptionStatus();
EXPECT_TRUE(s2.encrypted);
EXPECT_TRUE(s2.locked);
EXPECT_TRUE(s1.locked); // encrypt locks immediately
EXPECT_TRUE(c->unlockWallet("walletpass"));
const auto s2 = c->encryptionStatus();
EXPECT_TRUE(s2.encrypted);
EXPECT_FALSE(s2.locked);
EXPECT_TRUE(c->lockWallet());
const auto s3 = c->encryptionStatus();
EXPECT_TRUE(s3.encrypted);
EXPECT_FALSE(s3.locked);
EXPECT_TRUE(s3.locked);
EXPECT_TRUE(c->decryptWallet("walletpass").ok);
const auto s4 = c->encryptionStatus();