The app-wide "import-key pattern" fixes (missing guards / untrimmed input / no
re-entrancy protection), from the robustness audit:
- Import-key dialog: unify the type indicator and the RPC dispatch on one
classifier (classifyPrivateKey is now prefix-aware — an "SK..." shielded key no
longer misroutes to the transparent RPC), trim manual input (not just Paste),
and gate both the indicator and the Import button on a shared
isRecognizedPrivateKey() so unrecognized/empty input can't be submitted.
- Shield: guard the submit button on connected + !syncing (like Send), with a
disabled tooltip, instead of firing into a raw daemon error.
- Mining: disable the pool Mine button when the payout address is empty
("enter a payout address first"), and trim the pool URL/worker on persist.
- Console: track in-flight RPC commands (atomic counter + busy() override) so the
input is disabled while a command runs instead of piling up on the worker.
- Maintenance (rescan/repair/delete-chain/reinstall-daemon): re-entrancy guard —
bail with "already in progress" instead of launching a duplicate destructive op.
- Bootstrap dialog: re-attach to an in-flight download on reopen instead of
resetting state and orphaning the running worker.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
133 lines
5.8 KiB
C++
133 lines
5.8 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
//
|
|
// ConsoleCommandExecutor — the pluggable backend behind the shared ConsoleTab UI. It
|
|
// abstracts the only real differences between the full-node console (dragonxd log + RPC
|
|
// command execution) and the lite console (lite diagnostics ring + backend command),
|
|
// so both variants use one rich terminal widget instead of two implementations.
|
|
|
|
#pragma once
|
|
|
|
#include "console_channel.h"
|
|
#include "imgui.h"
|
|
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace dragonx {
|
|
class App;
|
|
namespace ui {
|
|
|
|
using ConsoleAddLineFn = std::function<void(const std::string&, ConsoleChannel)>;
|
|
|
|
struct ConsoleStatusLine {
|
|
std::string text;
|
|
ImU32 color = IM_COL32(200, 200, 200, 255);
|
|
bool pulse = false; // animate the toolbar dot (starting/connecting)
|
|
};
|
|
|
|
// Which log-filter toggles the toolbar should show for a given backend. The full node has a
|
|
// daemon/xmrig log + an RPC trace; the lite backend has neither (its diagnostics ring maps to
|
|
// the App/Error channels), so it only wants the errors-only + app-messages toggles.
|
|
struct ConsoleLogFilterCaps {
|
|
bool daemon = false; // daemon + xmrig output toggle
|
|
bool errorsOnly = false; // errors-only toggle
|
|
bool rpcTrace = false; // RPC method/source trace toggle
|
|
bool appMessages = false; // app / diagnostics log toggle
|
|
bool any() const { return daemon || errorsOnly || rpcTrace || appMessages; }
|
|
};
|
|
|
|
class ConsoleCommandExecutor {
|
|
public:
|
|
virtual ~ConsoleCommandExecutor() = default;
|
|
|
|
// True when commands can be run (RPC connected / lite wallet open).
|
|
virtual bool isReady() const = 0;
|
|
// A command is currently in flight (input is disabled while true).
|
|
virtual bool busy() const { return false; }
|
|
// Submit a user command for async execution. The UI has already echoed "> cmd" and
|
|
// handled the built-ins (clear/help/quit). Results arrive later via pollResult().
|
|
virtual void submit(const std::string& cmd) = 0;
|
|
// Pop one completed command result (raw text + error flag) if available; the UI
|
|
// formats/colors it via FormatConsoleRpcResultLines. Returns false if none pending.
|
|
virtual bool pollResult(std::string& result, bool& isError) = 0;
|
|
|
|
// Pull any new passive log lines (daemon/xmrig output, or the lite diagnostics ring)
|
|
// and hand each to `add`. Called once per frame.
|
|
virtual void pollLogLines(const ConsoleAddLineFn& add) { (void)add; }
|
|
|
|
// UI-chrome capabilities.
|
|
virtual bool hasRpcReference() const { return false; } // show the RPC command-reference popup
|
|
// Which log-filter toggles the toolbar should show (default: none).
|
|
virtual ConsoleLogFilterCaps logFilterCaps() const { return {}; }
|
|
|
|
// Print the backend-appropriate `help` output via `add`.
|
|
virtual void printHelp(const ConsoleAddLineFn& add) = 0;
|
|
|
|
// Extra status lines drawn above the toolbar (lite shows sync + last error).
|
|
virtual std::vector<ConsoleStatusLine> statusLines() const { return {}; }
|
|
// Short status shown in the toolbar (daemon state / lite connection). Empty text =>
|
|
// the toolbar shows its generic "no daemon" label.
|
|
virtual ConsoleStatusLine toolbarStatus() const { return {}; }
|
|
};
|
|
|
|
// ── Full-node: dragonxd log ingestion + RPC command execution ────────────────
|
|
class FullNodeConsoleExecutor : public ConsoleCommandExecutor {
|
|
public:
|
|
explicit FullNodeConsoleExecutor(App* app) : app_(app) {}
|
|
|
|
bool isReady() const override;
|
|
void submit(const std::string& cmd) override;
|
|
bool pollResult(std::string& result, bool& isError) override;
|
|
void pollLogLines(const ConsoleAddLineFn& add) override;
|
|
bool hasRpcReference() const override { return true; }
|
|
// Full node: daemon/xmrig log, errors-only, RPC trace, and app messages.
|
|
ConsoleLogFilterCaps logFilterCaps() const override { return {true, true, true, true}; }
|
|
void printHelp(const ConsoleAddLineFn& add) override;
|
|
ConsoleStatusLine toolbarStatus() const override;
|
|
// A command is in flight on the RPC worker — lets the UI disable the input (no queued pile-up).
|
|
bool busy() const override { return in_flight_.load() > 0; }
|
|
|
|
private:
|
|
App* app_;
|
|
std::atomic<int> in_flight_{0};
|
|
size_t last_daemon_output_size_ = 0;
|
|
size_t last_xmrig_output_size_ = 0;
|
|
int last_daemon_state_ = -1; // daemon::EmbeddedDaemon::State as int
|
|
bool last_rpc_connected_ = false;
|
|
std::deque<std::pair<std::string, bool>> results_; // {text, isError}
|
|
std::mutex results_mutex_;
|
|
};
|
|
|
|
// ── Lite: diagnostics ring + backend console command ─────────────────────────
|
|
class LiteConsoleExecutor : public ConsoleCommandExecutor {
|
|
public:
|
|
explicit LiteConsoleExecutor(App* app) : app_(app) {}
|
|
|
|
bool isReady() const override;
|
|
bool busy() const override;
|
|
void submit(const std::string& cmd) override;
|
|
bool pollResult(std::string& result, bool& isError) override;
|
|
void pollLogLines(const ConsoleAddLineFn& add) override;
|
|
// Lite: no daemon log / RPC trace — its diagnostics ring maps to the App + Error
|
|
// channels, so offer errors-only + app-messages (plus the always-shown text filter).
|
|
ConsoleLogFilterCaps logFilterCaps() const override { return {false, true, false, true}; }
|
|
void printHelp(const ConsoleAddLineFn& add) override;
|
|
std::vector<ConsoleStatusLine> statusLines() const override;
|
|
ConsoleStatusLine toolbarStatus() const override;
|
|
|
|
private:
|
|
App* app_;
|
|
std::uint64_t diag_gen_ = static_cast<std::uint64_t>(-1); // last consumed diagnostics generation
|
|
};
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|