// 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. The "real backend smoke test" the plan gates behind // passing fake-backend tests. // // lite_smoke [server-url] [--create] [--refresh] [--full] [--restore-recent] // // Read-only by default. --create initializes a new wallet. --refresh runs the refresh // commands through the parsers (shape check). --full also does a (slow) full sync first. // --restore-recent restores a throwaway wallet with a birthday near the tip so the sync is // minimal (seconds), then runs the data-shape checks — fast verification of balance/addresses/ // list shapes without a multi-minute full scan. Always run with an isolated HOME. #include "wallet/lite_client_bridge.h" #include "wallet/lite_connection_service.h" #include "wallet/lite_result_parsers.h" #include #include using namespace dragonx::wallet; // A standard valid 24-word BIP39 test mnemonic (no funds). Restored only into an isolated HOME. static const char* kTestSeed = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon " "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon " "abandon art"; // Push each refresh command's real output through its parser; print flags/counts, and the raw // (truncated) response only on a parse failure to diagnose real-shape mismatches. static void runDataShapeChecks(LiteClientBridge& bridge) { // Non-blocking commands first (read local wallet state); balance last (may need a sync). { auto r = bridge.execute("syncstatus", ""); auto p = parseLiteSyncStatusResponse(r.value); std::printf("[lite-smoke] syncstatus parse_ok=%d complete=%d synced=%llu/%llu\n", p.ok, p.syncStatus.complete, (unsigned long long)p.syncStatus.syncedBlocks, (unsigned long long)p.syncStatus.totalBlocks); if (!p.ok) std::printf("[lite-smoke] syncstatus RAW = %.150s\n", r.value.c_str()); } { 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()); if (!p.ok) std::printf("[lite-smoke] addresses RAW = %.150s\n", r.value.c_str()); } { 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()); if (!p.ok) std::printf("[lite-smoke] list RAW = %.150s\n", r.value.c_str()); } std::printf("[lite-smoke] balance (may block until synced)...\n"); { 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)); if (!p.ok) std::printf("[lite-smoke] balance RAW = %.150s\n", r.value.c_str()); } } int main(int argc, char** argv) { std::setvbuf(stdout, nullptr, _IONBF, 0); // unbuffered so output survives a timeout kill std::string server = "https://lite.dragonx.is"; bool doCreate = false, doRefresh = false, doFull = false, doRestoreRecent = 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 if (arg == "--restore-recent") doRestoreRecent = 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; } std::printf("[lite-smoke] walletExists(%s) = %s\n", kDragonXLiteChainName, bridge.walletExists(kDragonXLiteChainName) ? "true" : "false"); std::printf("[lite-smoke] checkServerOnline() = %s\n", bridge.checkServerOnline(server) ? "true" : "false"); if (doRestoreRecent) { // Bootstrap a client to learn the chain tip, then restore a throwaway wallet with a // birthday near the tip so the scan is minimal (seconds), then check data shapes. std::printf("[lite-smoke] bootstrap initializeNew() to learn tip...\n"); bridge.initializeNew(false, server); unsigned long long tip = 0; { auto r = bridge.execute("info", ""); auto p = parseLiteInfoResponse(r.value); tip = p.info.latestBlockHeight.has_value() ? (unsigned long long)*p.info.latestBlockHeight : 0; std::printf("[lite-smoke] info parse_ok=%d tip=%llu\n", p.ok, tip); } const unsigned long long birthday = tip > 10 ? tip - 10 : tip; std::printf("[lite-smoke] restore-from-phrase at birthday=%llu (minimal scan)...\n", birthday); auto rr = bridge.initializeNewFromPhrase(false, server, kTestSeed, birthday, 0, /*overwrite*/ true); std::printf("[lite-smoke] restore ok=%d\n", rr.ok); if (!rr.ok) std::printf("[lite-smoke] restore err = %s\n", rr.error.c_str()); // NB: this backend downloads from a fixed checkpoint regardless of birthday, so an // explicit full "sync" is slow. addresses/list/syncstatus read local state and don't // need it; balance may block until synced. std::printf("[lite-smoke] --- data shape check (no full sync) ---\n"); runDataShapeChecks(bridge); bridge.shutdown(); std::printf("[lite-smoke] done\n"); return 0; } 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) { 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) { 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)); } if (!doFull) { bridge.shutdown(); std::printf("[lite-smoke] (skipping balance/addresses/list; pass --full or --restore-recent)\n"); return 0; } std::printf("[lite-smoke] running full sync (blocking; can take many minutes)...\n"); bridge.execute("sync", ""); runDataShapeChecks(bridge); } bridge.shutdown(); std::printf("[lite-smoke] done\n"); return 0; }