Files
ObsidianDragon/src/app.h
DanS 4b9d6f7db5 fix(lite): rebuild controller on lite-server change (stale-settings audit HIGH)
The LiteWalletController was constructed once at App::init() with the lite
connection settings known at startup; changing the lite server in Settings
persisted to disk but never reached the live controller, so the new server had
no effect until the next launch.

Factor the construction into App::rebuildLiteWallet() and call it after a
successful server-selection save. The rebuild deliberately preserves a live
session: if a wallet is already open (and possibly mid-sync), it no-ops and the
new selection applies on the next controller build, rather than discarding the
open wallet and its uninterruptible in-flight sync.

Closes the last remaining HIGH from the session audit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 11:32:54 -05:00

683 lines
28 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#include <memory>
#include <string>
#include <functional>
#include <thread>
#include <atomic>
#include <chrono>
#include <unordered_map>
#include <unordered_set>
#include "data/transaction_history_cache.h"
#include "data/wallet_state.h"
#include "rpc/connection.h"
#include "services/network_refresh_service.h"
#include "services/wallet_security_controller.h"
#include "services/wallet_security_workflow.h"
#include "util/async_task_manager.h"
#include "wallet/wallet_capabilities.h"
#include "ui/sidebar.h"
#include "ui/windows/console_tab.h"
#include "imgui.h"
// Forward declarations
namespace dragonx {
namespace rpc {
class RPCClient;
class RPCWorker;
}
namespace config { class Settings; }
namespace daemon { class DaemonController; class EmbeddedDaemon; class XmrigManager; }
namespace util { class Bootstrap; class SecureVault; }
namespace wallet { class LiteWalletController; }
}
namespace dragonx {
/**
* @brief First-run wizard states
*/
enum class WizardPhase {
None, // No wizard — normal operation
BootstrapOffer, // Step 1: offer bootstrap download
BootstrapInProgress,// downloading / extracting
BootstrapFailed, // error with retry/skip option
Appearance, // Step 2: visual effects / performance options
EncryptOffer, // Step 3: offer wallet encryption
EncryptInProgress, // encrypting + daemon restarting
PinSetup, // Step 4: optional PIN setup (after encryption)
Done // wizard complete, launch normally
};
/**
* @brief Encrypt-wallet dialog phases (settings page flow)
*/
enum class EncryptDialogPhase {
PassphraseEntry, // Enter & confirm passphrase
Encrypting, // In-progress animation
PinSetup, // Offer PIN after successful encryption
Done // Finished — close dialog
};
/**
* @brief Main application class
*
* Manages application state, RPC connection, and coordinates UI rendering.
*/
class App {
public:
App();
~App();
// Non-copyable
App(const App&) = delete;
App& operator=(const App&) = delete;
/**
* @brief Initialize the application
* @return true if initialization succeeded
*/
bool init();
/**
* @brief Update application state (called every frame)
*/
void update();
/**
* @brief Pre-frame tasks that must run BEFORE ImGui::NewFrame().
*
* Font atlas rebuilds (hot-reload, user font scale changes) must
* happen before NewFrame() because NewFrame() caches font pointers.
* Rebuilding mid-frame causes dangling pointer crashes.
*/
void preFrame();
/**
* @brief Render the application UI (called every frame)
*/
void render();
/**
* @brief Clean shutdown
*/
void shutdown();
/**
* @brief Check if app should exit (render loop can stop)
* Enforces minimum 1-second display of shutdown screen so user sees feedback.
*/
bool shouldQuit() const {
if (!quit_requested_ || !shutdown_complete_) return false;
auto elapsed = std::chrono::steady_clock::now() - shutdown_start_time_;
return elapsed >= std::chrono::seconds(1);
}
/**
* @brief Request application exit — begins async shutdown
*/
void requestQuit();
/**
* @brief Begin graceful shutdown (called on window close)
* Starts daemon stop on a background thread while UI keeps rendering.
*/
void beginShutdown();
/**
* @brief Whether we are in the shutdown phase
*/
bool isShuttingDown() const { return shutting_down_; }
wallet::WalletCapabilities walletCapabilities() const { return wallet::currentWalletCapabilities(); }
bool isLiteBuild() const { return wallet::isLiteBuild(walletCapabilities()); }
bool supportsEmbeddedDaemon() const { return wallet::supportsEmbeddedDaemon(walletCapabilities()); }
bool supportsFullNodeLifecycleActions() const { return wallet::supportsFullNodeLifecycleActions(walletCapabilities()); }
bool supportsSoloMining() const { return wallet::supportsSoloMining(walletCapabilities()); }
bool supportsPoolMining() const { return wallet::supportsPoolMining(walletCapabilities()); }
bool supportsLiteBackend() const { return wallet::supportsLiteBackend(walletCapabilities()); }
/**
* @brief Render the shutdown overlay (called instead of normal UI during shutdown)
*/
void renderShutdownScreen();
/**
* @brief Render loading overlay in content area while daemon is starting/syncing
* @param contentH Height of the content area child window
*/
void renderLoadingOverlay(float contentH);
// Accessors for subsystems
rpc::RPCClient* rpc() { return rpc_.get(); }
rpc::RPCWorker* worker() { return worker_.get(); }
config::Settings* settings() { return settings_.get(); }
// Lite wallet controller (non-null only in lite builds with a linked backend).
wallet::LiteWalletController* liteWallet() { return lite_wallet_.get(); }
// (Re)build the lite controller from current settings so a changed lite-server selection
// takes effect. No-op on non-lite/unlinked builds; preserves a live wallet (see app.cpp).
void rebuildLiteWallet();
WalletState& state() { return state_; }
const WalletState& state() const { return state_; }
const WalletState& getWalletState() const { return state_; }
// Connection state (convenience wrappers)
bool isConnected() const { return state_.connected; }
int getBlockHeight() const { return state_.sync.blocks; }
const std::string& getConnectionStatus() const { return connection_status_; }
const std::string& getDaemonStatus() const { return daemon_status_; }
// Balance info (convenience wrappers)
double getShieldedBalance() const { return state_.shielded_balance; }
double getTransparentBalance() const { return state_.transparent_balance; }
double getTotalBalance() const { return state_.total_balance; }
double getUnconfirmedBalance() const { return state_.unconfirmed_balance; }
// Addresses
const std::vector<AddressInfo>& getZAddresses() const { return state_.z_addresses; }
const std::vector<AddressInfo>& getTAddresses() const { return state_.t_addresses; }
// Transactions
const std::vector<TransactionInfo>& getTransactions() const { return state_.transactions; }
// Mining
const MiningInfo& getMiningInfo() const { return state_.mining; }
void startMining(int threads);
void stopMining();
bool isMiningToggleInProgress() const { return mining_toggle_in_progress_.load(std::memory_order_relaxed); }
// Pool mining (xmrig)
void startPoolMining(int threads);
void stopPoolMining();
int getXmrigRequestedThreads() const {
return xmrig_manager_ ? xmrig_manager_->getRequestedThreads() : 0;
}
// Mine-when-idle state query
bool isIdleMiningActive() const { return idle_mining_active_; }
// Peers
const std::vector<PeerInfo>& getPeers() const { return state_.peers; }
const std::vector<BannedPeer>& getBannedPeers() const { return state_.bannedPeers; }
bool isPeerRefreshInProgress() const {
return network_refresh_.jobInProgress(services::NetworkRefreshService::Job::Peers);
}
void banPeer(const std::string& ip, int duration_seconds = 86400);
void unbanPeer(const std::string& ip);
void clearBans();
// Address operations
void createNewZAddress(std::function<void(const std::string&)> callback = nullptr);
void createNewTAddress(std::function<void(const std::string&)> callback = nullptr);
// Hide/unhide addresses from the address list (persisted in settings)
void hideAddress(const std::string& addr);
void unhideAddress(const std::string& addr);
bool isAddressHidden(const std::string& addr) const;
int getHiddenAddressCount() const;
void favoriteAddress(const std::string& addr);
void unfavoriteAddress(const std::string& addr);
bool isAddressFavorite(const std::string& addr) const;
// Address metadata (labels, icons, custom ordering)
void setAddressLabel(const std::string& addr, const std::string& label);
void setAddressIcon(const std::string& addr, const std::string& icon);
std::string getAddressLabel(const std::string& addr) const;
std::string getAddressIcon(const std::string& addr) const;
int getAddressSortOrder(const std::string& addr) const;
void setAddressSortOrder(const std::string& addr, int order);
int getNextSortOrder() const;
void swapAddressOrder(const std::string& a, const std::string& b);
bool isMiningAddress(const std::string& addr) const;
void setMiningAddress(const std::string& addr, bool mining);
void invalidateAddressValidationCache();
// Key export/import
void exportPrivateKey(const std::string& address, std::function<void(const std::string&)> callback);
void exportAllKeys(std::function<void(const std::string&)> callback);
void importPrivateKey(const std::string& key, std::function<void(bool, const std::string&)> callback);
// Wallet backup
void backupWallet(const std::string& destination, std::function<void(bool, const std::string&)> callback);
// Transaction operations
void sendTransaction(const std::string& from, const std::string& to,
double amount, double fee, const std::string& memo,
std::function<void(bool success, const std::string& result)> callback);
// Force refresh
void refreshNow();
void refreshMiningInfo();
void refreshPeerInfo();
void refreshMarketData();
/// @brief Per-category refresh intervals, adjusted by active tab
using RefreshIntervals = services::NetworkRefreshService::Intervals;
/// @brief Get recommended refresh intervals for a given page
static RefreshIntervals getIntervalsForPage(ui::NavPage page);
// UI navigation
void setCurrentPage(ui::NavPage page);
ui::NavPage getCurrentPage() const { return current_page_; }
// Dialog triggers (used by settings page to open modal dialogs)
void showImportKeyDialog() { show_import_key_ = true; }
void showExportKeyDialog() { show_export_key_ = true; }
void showBackupDialog() { show_backup_ = true; }
void showAboutDialog() { show_about_ = true; }
// Legacy tab compat — maps int to NavPage
void setCurrentTab(int tab);
int getCurrentTab() const { return static_cast<int>(current_page_); }
// Payment URI handling
void handlePaymentURI(const std::string& uri);
bool hasPendingPayment() const { return pending_payment_valid_; }
void clearPendingPayment() { pending_payment_valid_ = false; }
std::string getPendingToAddress() const { return pending_to_address_; }
double getPendingAmount() const { return pending_amount_; }
std::string getPendingMemo() const { return pending_memo_; }
std::string getPendingLabel() const { return pending_label_; }
// Embedded daemon control
bool startEmbeddedDaemon();
void stopEmbeddedDaemon();
bool isEmbeddedDaemonRunning() const;
bool isUsingEmbeddedDaemon() const { return supportsEmbeddedDaemon() && use_embedded_daemon_; }
void setUseEmbeddedDaemon(bool use) { use_embedded_daemon_ = use && supportsEmbeddedDaemon(); }
void rescanBlockchain(); // restart daemon with -rescan flag
void deleteBlockchainData(); // stop daemon, delete chain data, restart fresh
bool stopDaemonForBootstrap(); // stop daemon + disconnect for bootstrap, returns true if was running
bool isBootstrapDownloading() const { return bootstrap_downloading_; }
void setBootstrapDownloading(bool v) { bootstrap_downloading_ = v; }
// Get daemon memory usage in MB (uses embedded daemon handle if available,
// falls back to platform-level process scan for external daemons)
double getDaemonMemoryUsageMB() const;
// Diagnostic string describing daemon memory detection path (for debugging)
const std::string& getDaemonMemDiag() const { return daemon_mem_diag_; }
// Background gradient overlay texture
void setGradientTexture(ImTextureID tex) { gradient_tex_ = tex; }
ImTextureID getGradientTexture() const { return gradient_tex_; }
// Logo texture accessor (wallet branding icon)
ImTextureID getLogoTexture() const { return logo_tex_; }
int getLogoWidth() const { return logo_w_; }
int getLogoHeight() const { return logo_h_; }
// Coin logo texture accessor (DragonX currency icon for balance tab)
ImTextureID getCoinLogoTexture() const { return coin_logo_tex_; }
/**
* @brief Reload theme images (background gradient + logo) from new paths
* @param bgPath Path to background image override (empty = use default)
* @param logoPath Path to logo image override (empty = use default)
*/
void reloadThemeImages(const std::string& bgPath, const std::string& logoPath);
// Wizard / first-run
WizardPhase getWizardPhase() const { return wizard_phase_; }
bool isFirstRun() const;
/**
* @brief Stop daemon and re-run the setup wizard
* Called from Settings. Daemon restarts automatically when wizard completes.
*/
void restartWizard();
/**
* @brief Restart the embedded daemon (e.g. after changing debug categories)
* Shows "Restarting daemon..." in the loading overlay while the daemon cycles.
*/
void restartDaemon();
// Wallet encryption helpers
void encryptWalletWithPassphrase(const std::string& passphrase);
void unlockWallet(const std::string& passphrase, int timeout);
void lockWallet();
void changePassphrase(const std::string& oldPass, const std::string& newPass);
// Dialog triggers for encryption (from settings page)
void showEncryptDialog() {
show_encrypt_dialog_ = true;
encrypt_dialog_phase_ = EncryptDialogPhase::PassphraseEntry;
encrypt_status_.clear();
memset(encrypt_pass_buf_, 0, sizeof(encrypt_pass_buf_));
memset(encrypt_confirm_buf_, 0, sizeof(encrypt_confirm_buf_));
memset(enc_dlg_pin_buf_, 0, sizeof(enc_dlg_pin_buf_));
memset(enc_dlg_pin_confirm_buf_, 0, sizeof(enc_dlg_pin_confirm_buf_));
enc_dlg_saved_passphrase_.clear();
enc_dlg_pin_status_.clear();
}
void showChangePassphraseDialog() { show_change_passphrase_ = true; }
void showDecryptDialog() {
show_decrypt_dialog_ = true;
wallet_security_workflow_.reset();
memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
}
// Dialog triggers for PIN (from settings page)
void showPinSetupDialog() { show_pin_setup_ = true; pin_status_.clear(); }
void showPinChangeDialog() { show_pin_change_ = true; pin_status_.clear(); }
void showPinRemoveDialog() { show_pin_remove_ = true; pin_status_.clear(); }
bool hasPinVault() const;
/// @brief Check if RPC worker has queued results waiting to be processed
bool hasPendingRPCResults() const;
bool hasTransactionSendProgress() const { return send_progress_active_ || send_submissions_in_flight_ > 0 || !pending_opids_.empty(); }
std::string transactionSendProgressText() const;
std::string transactionRefreshProgressText() const;
bool isTransactionRefreshInProgress() const {
return network_refresh_.jobInProgress(services::NetworkRefreshService::Job::Transactions);
}
private:
friend class AppDaemonLifecycleRuntime;
friend class AppDaemonLifecycleTaskContext;
bool sendStopCommandSafely(rpc::RPCClient& client, const char* context);
void maybeFinishTransactionSendProgress();
void upsertPendingSendTransaction(const std::string& opid,
const std::string& from,
const std::string& to,
double amount,
const std::string& memo);
void markPendingSendTransactionSucceeded(const std::string& opid,
const std::string& txid);
void removePendingSendTransactions(const std::vector<std::string>& opids,
bool restoreBalances);
void applyPendingSendBalanceDeltas(bool includeAggregateBalances);
std::string transactionHistoryCacheWalletIdentity() const;
bool ensureTransactionHistoryCacheUnlockedFor(const std::string& walletIdentity);
void unlockTransactionHistoryCacheWithPassphrase(const std::string& passphrase);
void loadTransactionHistoryCacheIfAvailable();
void storeTransactionHistoryCacheIfAvailable();
void wipePendingTransactionHistoryCachePassphrase();
void resetTransactionHistoryCacheSession();
void pruneShieldedHistoryScanProgress();
void invalidateShieldedHistoryScanProgress(bool persistCache);
// Subsystems
std::unique_ptr<rpc::RPCClient> rpc_;
std::unique_ptr<rpc::RPCWorker> worker_;
// Fast-lane: dedicated RPC connection + worker for 1-second mining polls.
// Runs on its own thread with its own curl handle so it never blocks behind
// the main refresh batch.
std::unique_ptr<rpc::RPCClient> fast_rpc_;
std::unique_ptr<rpc::RPCWorker> fast_worker_;
// Saved connection credentials (needed to open the fast-lane connection)
rpc::ConnectionConfig saved_config_;
std::unique_ptr<config::Settings> settings_;
std::unique_ptr<wallet::LiteWalletController> lite_wallet_; // lite builds w/ linked backend
std::unique_ptr<daemon::DaemonController> daemon_controller_;
std::unique_ptr<daemon::XmrigManager> xmrig_manager_;
util::AsyncTaskManager async_tasks_;
bool pending_antivirus_dialog_ = false; // Show Windows Defender help dialog
// Wallet state
WalletState state_;
// Shutdown state
std::atomic<bool> shutting_down_{false};
std::atomic<bool> shutdown_complete_{false};
bool address_list_dirty_ = false; // P8: dedup rebuildAddressList
std::string shutdown_status_;
std::thread shutdown_thread_;
float shutdown_timer_ = 0.0f;
bool force_quit_confirm_ = false;
std::chrono::steady_clock::time_point shutdown_start_time_;
// Daemon restart (e.g. after changing debug log categories)
std::atomic<bool> daemon_restarting_{false};
// Encryption state check timeout
float encryption_check_timer_ = 0.0f;
// UI State
bool quit_requested_ = false;
bool show_demo_window_ = false;
bool show_settings_ = false;
bool show_about_ = false;
bool show_import_key_ = false;
bool show_export_key_ = false;
bool show_backup_ = false;
bool show_address_book_ = false;
// Embedded daemon state
bool use_embedded_daemon_ = wallet::supportsEmbeddedDaemon(wallet::currentWalletCapabilities());
std::string daemon_status_;
mutable std::string daemon_mem_diag_; // diagnostic info for daemon memory detection
size_t daemon_output_offset_ = 0; // for incremental output parsing (rescan detection)
// Export/Import state
std::string export_result_;
char import_key_input_[512] = {0};
std::string export_address_;
std::string import_status_;
bool import_success_ = false;
std::string backup_status_;
bool backup_success_ = false;
// Connection
std::string connection_status_ = "Disconnected";
bool connection_in_progress_ = false;
bool remote_rpc_plaintext_warning_shown_ = false;
float loading_timer_ = 0.0f; // spinner animation for loading overlay
// Current page (sidebar navigation)
ui::NavPage current_page_ = ui::NavPage::Overview;
ui::NavPage prev_page_ = ui::NavPage::Overview;
float page_alpha_ = 1.0f; // 0→1 fade on page switch
bool sidebar_collapsed_ = false; // true = icon-only mode
bool sidebar_user_toggled_ = false; // user manually toggled — suppress auto-collapse
float sidebar_width_anim_ = 0.0f; // animated width (0 = uninitialized)
float prev_dpi_scale_ = 0.0f; // detect DPI changes to snap sidebar width
// Background gradient overlay
ImTextureID gradient_tex_ = 0;
// Logo texture (reload on skin change / dark↔light switch)
ImTextureID logo_tex_ = 0;
int logo_w_ = 0;
int logo_h_ = 0;
bool logo_loaded_ = false;
bool logo_is_dark_variant_ = true; // tracks which variant is currently loaded
// Coin logo texture (DragonX currency icon, separate from wallet branding)
ImTextureID coin_logo_tex_ = 0;
int coin_logo_w_ = 0;
int coin_logo_h_ = 0;
bool coin_logo_loaded_ = false;
// Console tab
ui::ConsoleTab console_tab_;
// Pending payment from URI
bool pending_payment_valid_ = false;
std::string pending_to_address_;
double pending_amount_ = 0.0;
std::string pending_memo_;
std::string pending_label_;
// Per-category refresh timers, policy, and worker queue guards.
services::NetworkRefreshService network_refresh_;
int mining_slow_counter_ = 0; // counts fast ticks; fires slow refresh every N
// Mining toggle guard (prevents concurrent setgenerate calls)
std::atomic<bool> mining_toggle_in_progress_{false};
// Auto-shield guard (prevents concurrent auto-shield operations)
std::atomic<bool> auto_shield_pending_{false};
// P4: Incremental transaction cache
int last_tx_block_height_ = -1; // block height at last full tx fetch
static constexpr int MAX_VIEWTX_PER_CYCLE = 25; // cap z_viewtransaction calls per refresh
std::size_t shielded_history_scan_cursor_ = 0;
bool shielded_history_scan_pending_ = false;
std::unordered_map<std::string, int> shielded_history_scan_heights_;
// P4b: z_viewtransaction result cache — avoids re-calling the RPC for
// txids we've already enriched. Keyed by txid.
using ViewTxCacheEntry = services::NetworkRefreshService::TransactionViewCacheEntry;
services::NetworkRefreshService::TransactionViewCache viewtx_cache_;
// P4c: Confirmed transaction cache — deeply-confirmed txns (>= 10 confs)
// are accumulated here and reused across refresh cycles. Only
// recent/unconfirmed txns are re-fetched from the daemon each time.
std::vector<TransactionInfo> confirmed_tx_cache_;
std::unordered_set<std::string> confirmed_tx_ids_; // fast lookup
int confirmed_cache_block_ = -1; // block height when cache was last built
// Dirty flags for demand-driven refresh
bool addresses_dirty_ = true; // true → refreshAddresses() will run
bool address_validation_cache_dirty_ = true;
bool transactions_dirty_ = false; // true → force tx refresh regardless of block height
bool encryption_state_prefetched_ = false; // suppress duplicate getwalletinfo on connect
bool rescan_status_poll_in_progress_ = false;
bool opid_poll_in_progress_ = false;
// Pending z_sendmany operation tracking
bool send_progress_active_ = false;
int send_submissions_in_flight_ = 0;
std::vector<std::string> pending_opids_; // opids to poll for completion
struct PendingSendInfo {
std::string from;
std::string to;
std::string memo;
double amount = 0.0;
std::int64_t timestamp = 0;
};
std::unordered_map<std::string, PendingSendInfo> pending_send_info_;
// Txids from completed z_sendmany operations.
// Ensures shielded sends are discoverable by z_viewtransaction
// even when they don't appear in listtransactions or
// z_listreceivedbyaddress.
std::unordered_set<std::string> send_txids_;
// First-run wizard state
WizardPhase wizard_phase_ = WizardPhase::None;
std::unique_ptr<util::Bootstrap> bootstrap_;
bool bootstrap_downloading_ = false; // true while settings bootstrap dialog is active
std::string wizard_pending_passphrase_; // held until daemon connects
std::string wizard_saved_passphrase_; // held until PinSetup completes/skipped
// Wallet security flow state shared by wizard/settings encryption paths.
services::WalletSecurityController wallet_security_;
services::WalletSecurityWorkflow wallet_security_workflow_;
// Wizard: stopping an external daemon before bootstrap
bool wizard_stopping_external_ = false;
std::string wizard_stop_status_;
// PIN vault
std::unique_ptr<util::SecureVault> vault_;
data::TransactionHistoryCache transaction_history_cache_;
std::string pending_transaction_history_cache_passphrase_;
bool transaction_history_cache_loaded_ = false;
// Lock screen state
bool lock_screen_was_visible_ = false; // tracks lock→unlock transitions for auto-focus
bool lock_use_pin_ = true; // true = PIN input, false = passphrase input
char lock_pin_buf_[16] = {};
char lock_passphrase_buf_[256] = {};
std::string lock_error_msg_;
float lock_error_timer_ = 0.0f;
int lock_attempts_ = 0;
float lock_lockout_timer_ = 0.0f;
bool lock_unlock_in_progress_ = false;
// Encrypt wallet dialog state
bool show_encrypt_dialog_ = false;
bool show_change_passphrase_ = false;
EncryptDialogPhase encrypt_dialog_phase_ = EncryptDialogPhase::PassphraseEntry;
char encrypt_pass_buf_[256] = {};
char encrypt_confirm_buf_[256] = {};
char change_old_pass_buf_[256] = {};
char change_new_pass_buf_[256] = {};
char change_confirm_buf_[256] = {};
std::string encrypt_status_;
bool encrypt_in_progress_ = false;
std::string enc_dlg_saved_passphrase_; // held for PIN setup after encrypt
char enc_dlg_pin_buf_[16] = {};
char enc_dlg_pin_confirm_buf_[16] = {};
std::string enc_dlg_pin_status_;
// PIN setup dialog state (settings page)
bool show_pin_setup_ = false;
bool show_pin_change_ = false;
bool show_pin_remove_ = false;
char pin_buf_[16] = {};
char pin_confirm_buf_[16] = {};
char pin_old_buf_[16] = {};
char pin_passphrase_buf_[256] = {};
std::string pin_status_;
bool pin_in_progress_ = false;
// Decrypt wallet dialog state
bool show_decrypt_dialog_ = false;
char decrypt_pass_buf_[256] = {};
// Wizard PIN setup state
char wizard_pin_buf_[16] = {};
char wizard_pin_confirm_buf_[16] = {};
std::string wizard_pin_status_;
// Auto-lock on idle
std::chrono::steady_clock::time_point last_interaction_ = std::chrono::steady_clock::now();
// Mine-when-idle: auto-start/stop mining based on system idle state
bool idle_mining_active_ = false; // true when mining was auto-started by idle detection
bool idle_scaled_to_idle_ = false; // true when threads have been scaled up for idle
// Private methods - rendering
void renderStatusBar();
void renderAboutDialog();
void renderImportKeyDialog();
void renderExportKeyDialog();
void renderBackupDialog();
void renderFirstRunWizard();
void renderLockScreen();
void renderEncryptWalletDialog();
void renderDecryptWalletDialog();
void renderPinDialogs();
void renderAntivirusHelpDialog();
void processDeferredEncryption();
// Private methods - connection
void tryConnect();
void onConnected();
void onDisconnected(const std::string& reason);
void applyDefaultBanlist();
// Private methods - data refresh
void refreshData(); // Orchestrator: dispatches per-category refreshes
void refreshCoreData(); // Balance + blockchain info (can use fast_worker_)
void refreshAddressData(); // Address lists + balances
void refreshTransactionData(); // Transaction list + z_viewtransaction enrichment
void refreshRecentTransactionData(); // Lightweight recent/unconfirmed tx poll
bool refreshEncryptionState(); // Wallet encryption/lock state
void refreshBalance(); // Legacy: balance-only refresh (used by specific callers)
void refreshAddresses(); // Legacy: standalone address refresh
void refreshPrice();
void refreshWalletEncryptionState();
void applyRefreshPolicy(ui::NavPage page);
bool currentPageNeedsWalletDataRefresh() const;
bool shouldRunWalletTransactionRefresh() const;
bool shouldRefreshTransactions() const;
bool shouldRefreshRecentTransactions() const;
void checkAutoLock();
void checkIdleMining();
};
} // namespace dragonx