fix(lite): keep a rejected import key out of the log and Settings

The backend's error for a key it doesn't recognize repeats the key
("'<key>' was not recognized as either a spending key or a viewing
key"). Import passed that text on as its error, so a mistyped private
key was written to the lite log (shown in the Console tab) and to the
Settings status line. A failed import now carries a fixed reason
(unrecognized format, wallet locked, key already exists, could not be
decoded, or rejected), which is all the log records. Settings shows
"Couldn't import the key — check it, unlock the wallet (if encrypted)
and try again." instead, translated in all eight languages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 21:09:48 -05:00
co-authored by Claude Opus 5.5
parent 7c24400c83
commit bb84243114
13 changed files with 69 additions and 6 deletions
+40
View File
@@ -8376,6 +8376,45 @@ void testLiteWalletControllerRetireStopsOpFollowUps()
dragonx::test::g_liteFakeLocked = false;
}
// The backend's import error for an unrecognized key repeats the key ("'<key>' was not recognized ..."): the controller
// must never put that text in its result (shown in Settings) or in the lite log, only a fixed, non-secret reason.
void testLiteWalletControllerImportErrorNeverRepeatsKey()
{
using namespace dragonx::wallet;
const auto caps = makeWalletCapabilities(WalletBuildKind::Lite, false, true);
const auto conn = defaultLiteConnectionSettings();
dragonx::test::resetLiteFakeCounters();
LiteWalletController c(caps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
EXPECT_TRUE(c.createWallet(LiteWalletCreateRequest{}).walletReady);
for (int i = 0; i < 400 && !c.syncComplete(); ++i) std::this_thread::sleep_for(std::chrono::milliseconds(5));
dragonx::test::g_liteFakeImportFails = true;
const auto logMentions = [](const std::string& needle) {
for (const auto& line : LiteDiagnostics::instance().snapshot())
if (line.find(needle) != std::string::npos) return true;
return false;
};
{ // synchronous: both attempts fail; neither the result nor the log carries the key
const auto r = c.importKey("secret-extended-key-typoMARKSYNC");
EXPECT_FALSE(r.ok);
EXPECT_EQ(r.error, std::string("unrecognized key format"));
EXPECT_TRUE(r.error.find("MARKSYNC") == std::string::npos);
EXPECT_FALSE(logMentions("MARKSYNC"));
EXPECT_TRUE(logMentions("Key import failed: unrecognized key format"));
}
{ // async (the Settings path)
int n = 0; LiteImportResult got; got.ok = true;
EXPECT_TRUE(c.importKeyAsync("Kx1typoMARKASYNC", [&](const LiteImportResult& r) { ++n; got = r; }));
EXPECT_TRUE(c.waitWalletOpsIdle(std::chrono::milliseconds(3000)));
c.pumpWalletOps();
EXPECT_EQ(n, 1);
EXPECT_FALSE(got.ok);
EXPECT_TRUE(got.error.find("MARKASYNC") == std::string::npos);
EXPECT_FALSE(logMentions("MARKASYNC"));
}
dragonx::test::g_liteFakeImportFails = false;
}
void testLiteWalletControllerEncryptsBeforeSyncStarts()
{
using namespace dragonx::wallet;
@@ -13597,6 +13636,7 @@ int main()
testLiteWalletControllerWalletOpsAsync();
testLiteWalletControllerRetire();
testLiteWalletControllerRetireStopsOpFollowUps();
testLiteWalletControllerImportErrorNeverRepeatsKey();
testLiteWalletControllerEncryptsBeforeSyncStarts();
testLiteWalletControllerWorkerBacksOffOnSyncReadFailure();
testLiteWalletControllerDestroyWithBlockedOp();