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>
683 lines
28 KiB
C++
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
|