feat(lite): wire the create-time passphrase into the lite encrypt/unlock flow (W5-3)

The lite create/open/restore requests carried a passphrase field that the UI collected
(a labeled, masked "passphrase" input) but the backend initialize* calls never used —
so a user could believe their lite wallet was passphrase-protected at creation when it
did nothing. It now has a real meaning, wired in LiteWalletController:

- create / restore -> encryptWallet(passphrase): the backend encrypts + locks + saves
  the brand-new wallet.
- open -> unlockWallet(passphrase), but only when encryptionStatus() reports the existing
  wallet is actually encrypted + locked (no spurious unlock on an unencrypted wallet).

encryptWallet/unlockWallet take their own copy of the passphrase and wipe it; the
request copy is still wiped as before. A post-create encrypt failure is liteLog'd (the
wallet still exists, so the create is not failed).

Six existing lite-controller tests carried an incidental "hunter2" create passphrase from
when the field was dead; removed (they exercise non-encryption flows and want an
unencrypted wallet), and added testLiteWalletControllerCreateEncryptsWithPassphrase to
prove the new behavior. Completes the wallet-hardening P0-A cluster (7/7). ctest 1/1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 14:39:02 -05:00
parent 7e4822c021
commit c7d163f44a
3 changed files with 55 additions and 11 deletions

View File

@@ -4400,7 +4400,6 @@ void testLiteWalletControllerLifecycle()
EXPECT_FALSE(controller.walletOpen());
LiteWalletCreateRequest req;
req.passphrase = "hunter2";
const auto result = controller.createWallet(req);
EXPECT_TRUE(result.ok);
EXPECT_TRUE(result.walletReady);
@@ -4417,7 +4416,6 @@ void testLiteWalletControllerLifecycle()
dragonx::test::g_liteFakeWalletExists = true;
LiteWalletController controller(liteCaps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
LiteWalletOpenRequest req;
req.passphrase = "hunter2";
const auto result = controller.openWallet(req);
EXPECT_TRUE(result.ok);
EXPECT_TRUE(result.walletReady);
@@ -4492,7 +4490,6 @@ void testLiteWalletControllerM4()
auto c = std::make_unique<LiteWalletController>(
liteCaps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
LiteWalletCreateRequest req;
req.passphrase = "hunter2";
(void)c->createWallet(req);
return c;
};
@@ -4620,7 +4617,6 @@ void testLiteWalletControllerM5Persistence()
auto c = std::make_unique<LiteWalletController>(
liteCaps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
LiteWalletCreateRequest req;
req.passphrase = "hunter2";
(void)c->createWallet(req);
return c;
};
@@ -4702,7 +4698,6 @@ void testLiteWalletControllerEncryption()
auto c = std::make_unique<LiteWalletController>(
liteCaps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
LiteWalletCreateRequest req;
req.passphrase = "hunter2";
(void)c->createWallet(req);
return c;
};
@@ -4933,6 +4928,33 @@ void testLiteWalletControllerConsoleCommand()
// Async FULL lifecycle (Settings-page create/open/restore WITH passphrase/restore params) also
// fails over: the request runs off the UI thread against the preferred server, then the other
// usable defaults, finalized by pumpLifecycleResult() on the main thread.
// W5-3: a create-time passphrase now actually encrypts (and locks) the new lite wallet, and it
// unlocks with the same passphrase — previously the field was collected but ignored.
void testLiteWalletControllerCreateEncryptsWithPassphrase()
{
using namespace dragonx::wallet;
const auto liteCaps = makeWalletCapabilities(WalletBuildKind::Lite, false, true);
const LiteConnectionSettings conn = defaultLiteConnectionSettings();
dragonx::test::g_liteFakeEncrypted = false;
dragonx::test::g_liteFakeLocked = false;
auto c = std::make_unique<LiteWalletController>(
liteCaps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
LiteWalletCreateRequest req;
req.passphrase = "hunter2";
(void)c->createWallet(req);
const auto s = c->encryptionStatus();
EXPECT_TRUE(s.ok);
EXPECT_TRUE(s.encrypted); // the create-time passphrase encrypted the new wallet
EXPECT_TRUE(s.locked); // encrypt locks immediately
EXPECT_TRUE(c->unlockWallet("hunter2"));
const auto s2 = c->encryptionStatus();
EXPECT_FALSE(s2.locked);
}
void testLiteWalletControllerAsyncLifecycleFailover()
{
using namespace dragonx::wallet;
@@ -4961,7 +4983,6 @@ void testLiteWalletControllerAsyncLifecycleFailover()
LiteWalletController controller(liteCaps, conn,
LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
LiteWalletCreateRequest req;
req.passphrase = "hunter2";
EXPECT_TRUE(controller.beginCreateWalletAsync(req));
drain(controller);
EXPECT_TRUE(controller.walletOpen());
@@ -6919,6 +6940,7 @@ int main()
testLiteWalletControllerM4();
testLiteWalletControllerM5Persistence();
testLiteWalletControllerEncryption();
testLiteWalletControllerCreateEncryptsWithPassphrase();
testLiteChainNameMigration();
testLiteRefreshModelAppliesToWalletState();
testLiteSendShowsRecipientFromOutgoing();