Files
ObsidianDragon/tools/lite_smoke.cpp
DanS a8b5d2f6a3 feat(lite): M2b-3 — background refresh worker + App::update hook
- LiteWalletController owns a background std::thread worker that, once a wallet is ready,
  refreshes every ~4s and publishes a copyable LiteWalletAppRefreshModel under a mutex.
  Worker auto-starts on lifecycle-ready and is stopped+joined in the destructor. status_
  is written only on the main thread; walletOpen_/syncStarted_ are atomic.
- App::update() calls takeRefreshedModel() and applies it into state_ on the main thread
  (WalletState is non-copyable, so the model crosses the thread boundary, not the state),
  so the existing Balance/Receive/Transactions tabs populate from lite data.
- refreshWalletState() refactored onto refreshModel() (pure, worker-safe).
- testLiteWalletControllerWorkerProducesModel verifies the worker publishes a populated
  model (stable across repeated runs). Builds clean in all configs.

Real-backend smoke (lite_smoke --refresh now runs real output through the parsers) found
two integration bugs, documented in the plan for follow-up:
- syncstatus parser requires synced_blocks/total_blocks but the real idle response is
  {"syncing":"false"} (string), so it fails to parse when not actively syncing.
- the first data query (balance/list) blocks on a full chain sync, which would hang the
  worker's shutdown join — needs a cancel/timeout path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 22:38:34 -05:00

117 lines
5.4 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
//
// Real-backend smoke harness for the lite wallet bridge. Links the actual SDXL
// litelib_* backend (via the same imported CMake target the app uses) and exercises
// LiteClientBridge against a real lightwalletd server. This is the "real backend smoke
// test" the implementation plan gates behind passing fake-backend tests.
//
// lite_smoke [server-url] [--create]
//
// Read-only by default (available / checkServerOnline / walletExists). Pass --create to
// also attempt a real litelib_initialize_new (writes wallet state — run with an isolated
// HOME, e.g. HOME=/tmp/lite_smoke env, so it cannot clobber a real wallet).
#include "wallet/lite_client_bridge.h"
#include "wallet/lite_connection_service.h"
#include "wallet/lite_result_parsers.h"
#include <cstdio>
#include <string>
int main(int argc, char** argv)
{
using namespace dragonx::wallet;
std::setvbuf(stdout, nullptr, _IONBF, 0); // unbuffered so output survives a timeout kill
std::string server = "https://lite.dragonx.is";
bool doCreate = false;
bool doRefresh = false;
bool doFull = false;
for (int i = 1; i < argc; ++i) {
const std::string arg = argv[i];
if (arg == "--create") doCreate = true;
else if (arg == "--refresh") doRefresh = true;
else if (arg == "--full") doFull = true;
else server = arg;
}
std::printf("[lite-smoke] server = %s\n", server.c_str());
auto bridge = LiteClientBridge::linkedSdxl();
std::printf("[lite-smoke] available() = %s\n", bridge.available() ? "true" : "false");
if (!bridge.available()) {
std::printf("[lite-smoke] reason = %s\n", bridge.unavailableReason().c_str());
std::printf("[lite-smoke] FAIL: backend not linked\n");
return 2;
}
const bool walletExists = bridge.walletExists(kDragonXLiteChainName);
std::printf("[lite-smoke] walletExists(%s) = %s\n", kDragonXLiteChainName, walletExists ? "true" : "false");
const bool online = bridge.checkServerOnline(server);
std::printf("[lite-smoke] checkServerOnline() = %s\n", online ? "true" : "false");
if (doCreate) {
std::printf("[lite-smoke] initializeNew() ... (real network + writes wallet state)\n");
auto result = bridge.initializeNew(false, server);
std::printf("[lite-smoke] initializeNew ok = %s\n", result.ok ? "true" : "false");
if (result.ok) {
// The response is the new wallet's SEED PHRASE — never print it. Report only
// that a well-formed, non-empty response came back.
std::printf("[lite-smoke] wallet created; response len = %zu (seed redacted)\n",
result.value.size());
} else {
std::printf("[lite-smoke] error = %s\n", result.error.c_str());
}
}
if (doRefresh) {
// Run each refresh command through the real parser to verify the LIVE backend's
// JSON shapes match what the gateway expects. Prints flags/counts only (no secrets,
// addresses, or amounts).
std::printf("[lite-smoke] --- refresh shape check (real backend output -> parsers) ---\n");
{
auto r = bridge.execute("info", "");
auto p = parseLiteInfoResponse(r.value);
std::printf("[lite-smoke] info bridge_ok=%d parse_ok=%d err=%s\n",
r.ok, p.ok, liteResultParserErrorName(p.error));
}
{
auto r = bridge.execute("syncstatus", "");
auto p = parseLiteSyncStatusResponse(r.value);
std::printf("[lite-smoke] syncstatus bridge_ok=%d parse_ok=%d err=%s synced=%llu/%llu\n",
r.ok, p.ok, liteResultParserErrorName(p.error),
(unsigned long long)p.syncStatus.syncedBlocks, (unsigned long long)p.syncStatus.totalBlocks);
std::printf("[lite-smoke] syncstatus RAW = %.200s\n", r.value.c_str());
}
// The data commands below trigger a blocking full-chain sync on a fresh wallet;
// gate them behind --full so the shape check stays fast by default.
if (!doFull) { bridge.shutdown(); std::printf("[lite-smoke] (skipping balance/addresses/list; pass --full)\n"); return 0; }
{
auto r = bridge.execute("balance", "");
auto p = parseLiteBalanceResponse(r.value);
std::printf("[lite-smoke] balance bridge_ok=%d parse_ok=%d err=%s\n",
r.ok, p.ok, liteResultParserErrorName(p.error));
}
{
auto r = bridge.execute("addresses", "");
auto p = parseLiteAddressesResponse(r.value);
std::printf("[lite-smoke] addresses bridge_ok=%d parse_ok=%d err=%s z=%zu t=%zu\n",
r.ok, p.ok, liteResultParserErrorName(p.error),
p.addresses.zAddresses.size(), p.addresses.tAddresses.size());
}
{
auto r = bridge.execute("list", "");
auto p = parseLiteTransactionsResponse(r.value);
std::printf("[lite-smoke] list bridge_ok=%d parse_ok=%d err=%s count=%zu\n",
r.ok, p.ok, liteResultParserErrorName(p.error), p.transactions.transactions.size());
}
}
bridge.shutdown();
std::printf("[lite-smoke] done (real litelib_* symbols are callable; results above are live)\n");
return 0;
}