chore(lite): send-smoke harness + checkpoint generator
tools/lite_send_smoke drives the real SDXL backend's exact GUI send path (newaddr/status/list/tree/send/rescan) for diagnosing shielded sends. scripts/gen-lite-checkpoints.sh generates verified mainnet checkpoints from a synced dragonxd (getblockhash + getblockmerkletree), self-checking against a known checkpoint before emitting. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
133
tools/lite_send_smoke.cpp
Normal file
133
tools/lite_send_smoke.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
//
|
||||
// Real-backend SEND smoke harness for the lite wallet. Links the actual SDXL litelib_* backend
|
||||
// (same imported target the app uses) and drives the EXACT send path the GUI uses
|
||||
// (LiteClientBridge::execute("send", [{address,amount,memo}])), so it surfaces the same errors a
|
||||
// GUI send would. ALWAYS run with an isolated HOME — it reads/writes ~/.silentdragonxlite there.
|
||||
//
|
||||
// lite_send_smoke --newaddr [server] # create a new wallet, print receive addresses
|
||||
// lite_send_smoke --status [server] # open + sync + print balance/addresses
|
||||
// lite_send_smoke --send <drgx> [server] # open + sync + self-send <drgx> to own z-addr
|
||||
//
|
||||
// Receive addresses are PUBLIC (meant to be shared to receive funds) so they are printed; seeds and
|
||||
// private keys are never touched/printed here.
|
||||
|
||||
#include "wallet/lite_client_bridge.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace dragonx::wallet;
|
||||
using nlohmann::json;
|
||||
|
||||
// Recursively collect address-looking strings from any JSON shape the backend returns.
|
||||
static void collectAddrs(const json& n, std::vector<std::string>& zs, std::vector<std::string>& ts)
|
||||
{
|
||||
if (n.is_string()) {
|
||||
const std::string s = n.get<std::string>();
|
||||
if (s.rfind("zs", 0) == 0) zs.push_back(s);
|
||||
else if (!s.empty() && (s[0] == 'R' || s[0] == 't')) ts.push_back(s);
|
||||
} else if (n.is_array()) {
|
||||
for (const auto& e : n) collectAddrs(e, zs, ts);
|
||||
} else if (n.is_object()) {
|
||||
for (const auto& kv : n.items()) collectAddrs(kv.value(), zs, ts);
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
std::string mode;
|
||||
double sendDrgx = 0.0;
|
||||
bool doRescan = false;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
const std::string a = argv[i];
|
||||
if (a == "--newaddr" || a == "--status" || a == "--list" || a == "--tree") mode = a;
|
||||
else if (a == "--send") { mode = a; if (i + 1 < argc) sendDrgx = std::stod(argv[++i]); }
|
||||
else if (a == "--rescan") doRescan = true; // force witness rebuild from the closest checkpoint
|
||||
else server = a;
|
||||
}
|
||||
if (mode.empty()) { std::printf("usage: lite_send_smoke --newaddr|--status|--send <drgx> [server]\n"); return 2; }
|
||||
|
||||
auto bridge = LiteClientBridge::linkedSdxl();
|
||||
std::printf("[send-smoke] server = %s\n", server.c_str());
|
||||
std::printf("[send-smoke] available() = %s\n", bridge.available() ? "true" : "false");
|
||||
if (!bridge.available()) { std::printf("[send-smoke] FAIL: backend not linked (%s)\n",
|
||||
bridge.unavailableReason().c_str()); return 2; }
|
||||
std::printf("[send-smoke] serverOnline = %s\n", bridge.checkServerOnline(server) ? "true" : "false");
|
||||
|
||||
if (mode == "--newaddr") {
|
||||
auto r = bridge.initializeNew(false, server);
|
||||
std::printf("[send-smoke] initializeNew ok=%d %s\n", r.ok, r.ok ? "" : r.error.c_str());
|
||||
if (!r.ok) return 1;
|
||||
} else {
|
||||
auto r = bridge.initializeExisting(false, server);
|
||||
std::printf("[send-smoke] initializeExisting ok=%d %s\n", r.ok, r.ok ? "" : r.error.c_str());
|
||||
if (!r.ok) return 1;
|
||||
if (doRescan) {
|
||||
std::printf("[send-smoke] rescanning (rebuild witnesses from closest checkpoint)...\n");
|
||||
auto rs = bridge.execute("rescan", "");
|
||||
std::printf("[send-smoke] rescan ok=%d %.160s\n", rs.ok, rs.value.c_str());
|
||||
} else {
|
||||
std::printf("[send-smoke] syncing (may take a while)...\n");
|
||||
auto s = bridge.execute("sync", "");
|
||||
std::printf("[send-smoke] sync ok=%d\n", s.ok);
|
||||
}
|
||||
auto b = bridge.execute("balance", "");
|
||||
std::printf("[send-smoke] balance = %.400s\n", b.value.c_str());
|
||||
}
|
||||
|
||||
// Addresses (public — safe to print).
|
||||
std::vector<std::string> zs, ts;
|
||||
{
|
||||
auto a = bridge.execute("addresses", "");
|
||||
json j = json::parse(a.value, nullptr, /*allow_exceptions*/ false);
|
||||
if (!j.is_discarded()) collectAddrs(j, zs, ts);
|
||||
std::printf("[send-smoke] addresses: z=%zu t=%zu\n", zs.size(), ts.size());
|
||||
for (const auto& z : zs) std::printf("[send-smoke] Z (fund this for z2z): %s\n", z.c_str());
|
||||
for (const auto& t : ts) std::printf("[send-smoke] T (transparent) : %s\n", t.c_str());
|
||||
if (zs.empty() && ts.empty()) std::printf("[send-smoke] addresses RAW = %.200s\n", a.value.c_str());
|
||||
}
|
||||
|
||||
if (mode == "--list") {
|
||||
auto l = bridge.execute("list", "");
|
||||
std::printf("[send-smoke] list = %.3000s\n", l.value.c_str());
|
||||
}
|
||||
|
||||
if (mode == "--tree") {
|
||||
// Dump the wallet's latest Sapling commitment tree to diff against the node's
|
||||
// `getblockmerkletree <height>` — a mismatch localizes a tree-build divergence.
|
||||
auto t = bridge.execute("saplingtree", "");
|
||||
std::printf("[send-smoke] saplingtree = %s\n", t.value.c_str());
|
||||
}
|
||||
|
||||
if (mode == "--newaddr") {
|
||||
std::printf("[send-smoke] save ok=%d\n", bridge.execute("save", "").ok);
|
||||
} else if (mode == "--send") {
|
||||
if (zs.empty()) { std::printf("[send-smoke] FAIL: no z-address to self-send to\n"); bridge.shutdown(); return 1; }
|
||||
const long long zat = static_cast<long long>(std::llround(sendDrgx * 1e8));
|
||||
json arr = json::array();
|
||||
json o; o["address"] = zs.front(); o["amount"] = zat; o["memo"] = "lite send smoke";
|
||||
arr.push_back(std::move(o));
|
||||
std::printf("[send-smoke] SEND %.8f DRGX (%lld zat) -> own z-addr ...\n", sendDrgx, zat);
|
||||
std::printf("[send-smoke] send args = %s\n", arr.dump().c_str());
|
||||
auto res = bridge.execute("send", arr.dump());
|
||||
std::printf("[send-smoke] send bridge_ok=%d\n", res.ok);
|
||||
std::printf("[send-smoke] send RESULT = %.600s\n", res.value.c_str());
|
||||
if (!res.error.empty()) std::printf("[send-smoke] send ERROR = %s\n", res.error.c_str());
|
||||
std::printf("[send-smoke] save ok=%d\n", bridge.execute("save", "").ok);
|
||||
}
|
||||
|
||||
bridge.shutdown();
|
||||
std::printf("[send-smoke] done\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user