feat(lite): real backend integration — controller, M0-M2a wiring, smoke tool, tests

- LiteWalletController (src/wallet/lite_wallet_controller.*): App-owned; runs real
  create/open/restore via the linked SDXL bridge with allowBridgeCalls=true; wipes
  seed/passphrase with sodium_memzero; persists on a ready wallet. M2a:
  applyLiteRefreshModelToWalletState maps a parsed refresh bundle into WalletState
  (zatoshi->DRGX, z/t split, tx typing + confirmations, sync progress).
- App wiring: liteWallet() accessor + init() construction when supportsLiteBackend();
  persist -> settings save.
- settings_page: "Validate" reroutes to the controller for real execution (validation-
  only fallback otherwise); wipes UI secret buffers after submit.
- chain name default -> "main" with load-time migration of legacy "DRAGONX"
  (settings.cpp), preventing the backend "Unknown chain" panic.
- M0: build.sh --lite-backend flag; lite_smoke real-backend tool + CMake targets;
  tests/fake_lite_backend.h deterministic harness.
- Tests (test_phase4): injectable-fake bridge, controller lifecycle, chain-name
  migration, refresh->WalletState mapping; plus the lite test-suite churn-cleanup rewrite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 21:15:44 -05:00
parent 863d015628
commit 5586f334a4
12 changed files with 3597 additions and 121 deletions

View File

@@ -11,6 +11,8 @@
#include "rpc/rpc_worker.h"
#include "rpc/connection.h"
#include "config/settings.h"
#include "wallet/lite_wallet_controller.h"
#include "wallet/lite_wallet_server_selection_adapter.h"
#include "daemon/daemon_controller.h"
#include "daemon/embedded_daemon.h"
#include "daemon/lifecycle_adapters.h"
@@ -164,6 +166,16 @@ bool App::init()
settings_->clearUpgradeSave();
}
// Lite builds with a linked SDXL backend own a lite wallet controller that drives
// real create/open/restore through the bridge. Full-node and unlinked-lite builds
// leave lite_wallet_ null (the UI falls back to validation-only).
if (supportsLiteBackend()) {
lite_wallet_ = wallet::LiteWalletController::createLinked(
walletCapabilities(),
wallet::liteConnectionSettingsFromAppSettings(*settings_));
lite_wallet_->setPersistCallback([this]() { settings_->save(); });
}
// Apply verbose logging preference from saved settings
util::Logger::instance().setVerbose(settings_->getVerboseLogging());
@@ -1126,10 +1138,7 @@ void App::render()
// Tabs that need balance/transaction data (show overlay):
// Overview, Send, Receive, History
// ---------------------------------------------------------------
bool pageNeedsWalletData = (current_page_ == ui::NavPage::Overview ||
current_page_ == ui::NavPage::Send ||
current_page_ == ui::NavPage::Receive ||
current_page_ == ui::NavPage::History);
bool pageNeedsWalletData = wallet::uiSurfaceNeedsWalletData(ui::NavPageSurface(current_page_));
bool daemonReady = state_.connected && !state_.warming_up;
// Don't show lock screen while pool mining — xmrig runs independently
@@ -1282,11 +1291,7 @@ void App::render()
}
}
};
// Fade content area window draw list (fills, text, borders)
fadeVerts(caDL, caVtxStart, caDL->VtxBuffer.Size);
// Fade ForegroundDrawList panel effects (rainbow border, edge trace,
// Apply to foreground panel effects (rainbow borders, edge trace,
// shimmer, specular glare) — but NOT viewport-wide effects (embers,
// overlay) which were added after fgVtxEnd.
fadeVerts(fgDL, fgVtxStart, fgVtxEnd);
@@ -2099,7 +2104,12 @@ void App::setCurrentTab(int tab) {
bool App::startEmbeddedDaemon()
{
if (!use_embedded_daemon_) {
if (!supportsEmbeddedDaemon()) {
DEBUG_LOGF("Embedded daemon support unavailable in this build, not starting\n");
return false;
}
if (!isUsingEmbeddedDaemon()) {
DEBUG_LOGF("Embedded daemon disabled, not starting\n");
return false;
}
@@ -2309,6 +2319,11 @@ bool App::isEmbeddedDaemonRunning() const
void App::rescanBlockchain()
{
if (!supportsFullNodeLifecycleActions()) {
ui::Notifications::instance().warning("Full-node lifecycle actions are unavailable in lite build");
return;
}
auto decision = daemon::DaemonController::evaluateLifecycleOperation(
daemon::DaemonController::LifecycleOperation::Rescan,
isUsingEmbeddedDaemon(), daemon_controller_ != nullptr, isEmbeddedDaemonRunning());
@@ -2343,6 +2358,11 @@ void App::rescanBlockchain()
void App::deleteBlockchainData()
{
if (!supportsFullNodeLifecycleActions()) {
ui::Notifications::instance().warning("Full-node lifecycle actions are unavailable in lite build");
return;
}
auto decision = daemon::DaemonController::evaluateLifecycleOperation(
daemon::DaemonController::LifecycleOperation::DeleteBlockchainData,
isUsingEmbeddedDaemon(), daemon_controller_ != nullptr, isEmbeddedDaemonRunning());
@@ -2366,6 +2386,10 @@ void App::deleteBlockchainData()
bool App::stopDaemonForBootstrap()
{
if (!supportsFullNodeLifecycleActions()) {
return false;
}
auto decision = daemon::DaemonController::evaluateLifecycleOperation(
daemon::DaemonController::LifecycleOperation::BootstrapStop,
isUsingEmbeddedDaemon(), daemon_controller_ != nullptr, isEmbeddedDaemonRunning());
@@ -3152,9 +3176,14 @@ void App::maybeFinishTransactionSendProgress()
}
void App::restartDaemon()
{
if (!supportsFullNodeLifecycleActions()) {
ui::Notifications::instance().warning("Full-node lifecycle actions are unavailable in lite build");
return;
}
auto decision = daemon::DaemonController::evaluateLifecycleOperation(
daemon::DaemonController::LifecycleOperation::ManualRestart,
use_embedded_daemon_, daemon_controller_ != nullptr, isEmbeddedDaemonRunning(), daemon_restarting_.load());
isUsingEmbeddedDaemon(), daemon_controller_ != nullptr, isEmbeddedDaemonRunning(), daemon_restarting_.load());
if (!decision.allowed) return;
daemon_restarting_ = true;