feat(lite): Console tab with connection + open/create diagnostics

The lite variant had no visibility into why a wallet failed to open — just a
"disconnected" spinner. Add a lite-only Console tab (full-node keeps its RPC
console) that shows a live diagnostic log.

- LiteDiagnostics: a small thread-safe, bounded ring buffer (header-only). The
  controller writes to it from its background threads: each failover server
  attempt and result, wallet open/create/restore outcomes, sync start, and
  blocked-open reasons. The App logs controller (re)builds with the preferred
  server.
- lite_console_tab: a terminal-styled, read-only view of the log (newest at the
  bottom, error/success lines coloured) with Clear / Copy / Auto-scroll. Reachable
  even when the wallet is locked (it's diagnostics, no secrets). Registered as
  NavPage::LiteConsole, gated lite-only via WalletUiSurface::LiteConsole.

A unit test drives an open-with-failover and asserts the log records the
connection attempt and the successful open. Built clean for full-node, lite, and
Windows cross-compile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 18:46:25 -05:00
parent dbeae3ac98
commit 85a1080b52
10 changed files with 258 additions and 7 deletions

View File

@@ -25,6 +25,7 @@
#include "util/xmrig_updater.h"
#include "util/lite_server_probe.h"
#include "wallet/lite_connection_service.h"
#include "wallet/lite_diagnostics.h"
#include "wallet/lite_owned_string.h"
#include "wallet/lite_rollout_policy.h"
#include "wallet/lite_wallet_controller.h"
@@ -3560,6 +3561,7 @@ void testLiteWalletControllerOpenFailover()
dragonx::test::resetLiteFakeCounters();
dragonx::test::g_liteFakeWalletExists = true;
dragonx::test::g_liteFakeDeadServerSubstr = "dead.example";
LiteDiagnostics::instance().clear(); // verify the open populates the console log
LiteWalletController controller(liteCaps, conn,
LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
EXPECT_TRUE(controller.beginOpenExisting());
@@ -3569,6 +3571,17 @@ void testLiteWalletControllerOpenFailover()
controller.pumpAsyncOpen();
EXPECT_TRUE(controller.walletOpen());
EXPECT_TRUE(controller.lastOpenError().empty());
// The Console tab reads this log: the failover attempt + success must be recorded.
const auto log = LiteDiagnostics::instance().snapshot();
EXPECT_TRUE(!log.empty());
bool sawConnecting = false, sawOpened = false;
for (const auto& l : log) {
if (l.find("connecting to") != std::string::npos) sawConnecting = true;
if (l.find("Wallet opened via") != std::string::npos) sawOpened = true;
}
EXPECT_TRUE(sawConnecting);
EXPECT_TRUE(sawOpened);
}
// All servers dead -> open fails, wallet stays closed, reason surfaced.