Files
ObsidianDragon/src/config/settings.h
DanS 592ad4fef6 feat(lite): ObsidianDragonLite Network tab — server browser
A lite-wallet-only "Network" tab (full-node keeps the Peers tab; exactly one shows per variant)
to manage lightwalletd servers, replacing the basic selector that was in Settings.

- Card list of servers with per-server latency + status dot, DNS host + resolved IP, and an
  Official/Custom pill. Official DragonX servers get a glowing outline.
- Pick a server (Sticky) by clicking its card, or toggle "use a random server" (Random mode);
  selection applies immediately (App::rebuildLiteWallet(force=true) tears down + rebuilds the
  controller against the new server and resyncs — its dtor detaches the uninterruptible sync
  thread, so this doesn't block).
- Add custom servers; hide/unhide servers (persisted set, revealed by a "Show hidden" toggle).
- Latency/IP come from a new background probe (util/LiteServerProbe): libcurl CONNECT_ONLY does
  the TCP+TLS handshake (works for gRPC lightwalletd, no HTTP response needed), recording
  APPCONNECT_TIME as latency and CURLINFO_PRIMARY_IP. Auto-runs on tab open + a Refresh button.

Wiring: WalletUiSurface::LiteNetwork (gated !fullNodePagesAvailable) + NavPage::LiteNetwork in
the sidebar + app.cpp dispatch; settings gains a hidden-servers set; isOfficialLiteServer() added
to lite_connection_service. The Settings page lite-server selector + its plumbing are removed
(single source of truth = the tab).

Reuses the existing server model (LiteServerPreference, Sticky/Random, selectLiteServer) and UI
primitives (DrawGlassPanel, ThemeEffects glow, peers-tab ping-dot idiom). Unit-tested
(liteServerHost, isOfficialLiteServer) + an env-gated live probe (verified vs lite.dragonx.is:
online, latency, IP). Both variants + lite-backend build; suite passes; hygiene clean; GUI
smoke-launched without crash.

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

466 lines
21 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#include <algorithm>
#include <cstddef>
#include <map>
#include <string>
#include <set>
#include <vector>
namespace dragonx {
namespace config {
/**
* @brief Application settings manager
*
* Handles loading and saving of user preferences.
*/
class Settings {
public:
Settings();
~Settings();
/**
* @brief Load settings from default location
* @return true if loaded successfully
*/
bool load();
/**
* @brief Load settings from specific path
* @param path Path to settings file
* @return true if loaded successfully
*/
bool load(const std::string& path);
/**
* @brief Save settings to default location
* @return true if saved successfully
*/
bool save();
/**
* @brief Save settings to specific path
* @param path Path to settings file
* @return true if saved successfully
*/
bool save(const std::string& path);
/**
* @brief Get default settings file path
*/
static std::string getDefaultPath();
enum class LiteServerSelectionPreferenceMode {
Sticky,
Random
};
struct LiteServerPreference {
std::string url;
std::string label;
bool enabled = true;
};
// Theme
std::string getTheme() const { return theme_; }
void setTheme(const std::string& theme) { theme_ = theme; }
// Unified skin
std::string getSkinId() const { return skin_id_; }
void setSkinId(const std::string& id) { skin_id_ = id; }
// Privacy
bool getSaveZtxs() const { return save_ztxs_; }
void setSaveZtxs(bool save) { save_ztxs_ = save; }
bool getAutoShield() const { return auto_shield_; }
void setAutoShield(bool shield) { auto_shield_ = shield; }
bool getUseTor() const { return use_tor_; }
void setUseTor(bool tor) { use_tor_ = tor; }
// Fees
bool getAllowCustomFees() const { return allow_custom_fees_; }
void setAllowCustomFees(bool allow) { allow_custom_fees_ = allow; }
double getDefaultFee() const { return default_fee_; }
void setDefaultFee(double fee) { default_fee_ = fee; }
// Price
bool getFetchPrices() const { return fetch_prices_; }
void setFetchPrices(bool fetch) { fetch_prices_ = fetch; }
// Explorer URLs
std::string getTxExplorerUrl() const { return tx_explorer_url_; }
void setTxExplorerUrl(const std::string& url) { tx_explorer_url_ = url; }
std::string getAddressExplorerUrl() const { return address_explorer_url_; }
void setAddressExplorerUrl(const std::string& url) { address_explorer_url_ = url; }
// Language
std::string getLanguage() const { return language_; }
void setLanguage(const std::string& lang) { language_ = lang; }
// Visual effects
bool getAcrylicEnabled() const { return acrylic_enabled_; }
void setAcrylicEnabled(bool v) { acrylic_enabled_ = v; }
int getAcrylicQuality() const { return acrylic_quality_; }
void setAcrylicQuality(int v) { acrylic_quality_ = v; }
float getBlurMultiplier() const { return blur_multiplier_; }
void setBlurMultiplier(float v) { blur_multiplier_ = v; }
bool getReducedTransparency() const { return ui_opacity_ >= 0.99f; }
void setReducedTransparency(bool v) { if (v) ui_opacity_ = 1.0f; }
float getUIOpacity() const { return ui_opacity_; }
void setUIOpacity(float v) { ui_opacity_ = std::max(0.3f, std::min(1.0f, v)); }
float getWindowOpacity() const { return window_opacity_; }
void setWindowOpacity(float v) { window_opacity_ = std::max(0.3f, std::min(1.0f, v)); }
float getNoiseOpacity() const { return noise_opacity_; }
void setNoiseOpacity(float v) { noise_opacity_ = v; }
// Gradient background mode (use gradient variant of theme background)
bool getGradientBackground() const { return gradient_background_; }
void setGradientBackground(bool v) { gradient_background_ = v; }
// Balance layout (string ID, e.g. "classic", "donut")
std::string getBalanceLayout() const { return balance_layout_; }
void setBalanceLayout(const std::string& v) { balance_layout_ = v; }
// Console scanline effect
bool getScanlineEnabled() const { return scanline_enabled_; }
void setScanlineEnabled(bool v) { scanline_enabled_ = v; }
// Hidden addresses (addresses hidden from the UI by the user)
const std::set<std::string>& getHiddenAddresses() const { return hidden_addresses_; }
bool isAddressHidden(const std::string& addr) const { return hidden_addresses_.count(addr) > 0; }
void hideAddress(const std::string& addr) { hidden_addresses_.insert(addr); }
void unhideAddress(const std::string& addr) { hidden_addresses_.erase(addr); }
int getHiddenAddressCount() const { return (int)hidden_addresses_.size(); }
// Favorite addresses (pinned to top of address list)
const std::set<std::string>& getFavoriteAddresses() const { return favorite_addresses_; }
bool isAddressFavorite(const std::string& addr) const { return favorite_addresses_.count(addr) > 0; }
void favoriteAddress(const std::string& addr) { favorite_addresses_.insert(addr); }
void unfavoriteAddress(const std::string& addr) { favorite_addresses_.erase(addr); }
int getFavoriteAddressCount() const { return (int)favorite_addresses_.size(); }
// Address metadata (labels, icons, custom ordering)
struct AddressMeta {
std::string label;
std::string icon; // material icon name, e.g. "savings"
int sortOrder = -1; // -1 = auto (use default sort)
bool mining = false;
};
const AddressMeta& getAddressMeta(const std::string& addr) const {
static const AddressMeta empty{};
auto it = address_meta_.find(addr);
return it != address_meta_.end() ? it->second : empty;
}
void setAddressLabel(const std::string& addr, const std::string& label) {
address_meta_[addr].label = label;
}
void setAddressIcon(const std::string& addr, const std::string& icon) {
address_meta_[addr].icon = icon;
}
void setAddressSortOrder(const std::string& addr, int order) {
address_meta_[addr].sortOrder = order;
}
bool isMiningAddress(const std::string& addr) const {
auto it = address_meta_.find(addr);
return it != address_meta_.end() && it->second.mining;
}
void setMiningAddress(const std::string& addr, bool mining) {
address_meta_[addr].mining = mining;
}
std::set<std::string> getMiningAddresses() const {
std::set<std::string> addresses;
for (const auto& [addr, meta] : address_meta_) {
if (meta.mining) addresses.insert(addr);
}
return addresses;
}
int getNextSortOrder() const {
int mx = -1;
for (const auto& [k, v] : address_meta_)
if (v.sortOrder > mx) mx = v.sortOrder;
return mx + 1;
}
void swapAddressOrder(const std::string& a, const std::string& b) {
int oa = address_meta_[a].sortOrder;
int ob = address_meta_[b].sortOrder;
address_meta_[a].sortOrder = ob;
address_meta_[b].sortOrder = oa;
}
// First-run wizard
bool getWizardCompleted() const { return wizard_completed_; }
void setWizardCompleted(bool v) { wizard_completed_ = v; }
// Security — auto-lock timeout (seconds; 0 = disabled)
int getAutoLockTimeout() const { return auto_lock_timeout_; }
void setAutoLockTimeout(int seconds) { auto_lock_timeout_ = seconds; }
// Security — unlock duration (seconds) for walletpassphrase
int getUnlockDuration() const { return unlock_duration_; }
void setUnlockDuration(int seconds) { unlock_duration_ = seconds; }
// Security — PIN unlock enabled
bool getPinEnabled() const { return pin_enabled_; }
void setPinEnabled(bool v) { pin_enabled_ = v; }
// Daemon — keep running in background when closing the app
bool getKeepDaemonRunning() const { return keep_daemon_running_; }
void setKeepDaemonRunning(bool v) { keep_daemon_running_ = v; }
// Daemon — stop externally-started daemons on exit (default: false)
bool getStopExternalDaemon() const { return stop_external_daemon_; }
void setStopExternalDaemon(bool v) { stop_external_daemon_ = v; }
// Daemon — maximum peer connections (0 = daemon default)
int getMaxConnections() const { return max_connections_; }
void setMaxConnections(int v) { max_connections_ = std::max(0, v); }
// Lite wallet server selection
LiteServerSelectionPreferenceMode getLiteServerSelectionMode() const { return lite_server_selection_mode_; }
void setLiteServerSelectionMode(LiteServerSelectionPreferenceMode mode) { lite_server_selection_mode_ = mode; }
std::string getLiteStickyServerUrl() const { return lite_sticky_server_url_; }
void setLiteStickyServerUrl(const std::string& url) { lite_sticky_server_url_ = url; }
std::string getLiteChainName() const { return lite_chain_name_; }
void setLiteChainName(const std::string& chainName) { lite_chain_name_ = chainName; }
std::size_t getLiteRandomSelectionSeed() const { return lite_random_selection_seed_; }
void setLiteRandomSelectionSeed(std::size_t seed) { lite_random_selection_seed_ = seed; }
bool getLitePersistSelectedServer() const { return lite_persist_selected_server_; }
void setLitePersistSelectedServer(bool persist) { lite_persist_selected_server_ = persist; }
const std::vector<LiteServerPreference>& getLiteServers() const { return lite_servers_; }
void setLiteServers(const std::vector<LiteServerPreference>& servers) { lite_servers_ = servers; }
// Lite servers the user has hidden from the Network tab (kept by URL, shown via a toggle).
const std::set<std::string>& getLiteHiddenServers() const { return lite_hidden_servers_; }
bool isLiteServerHidden(const std::string& url) const { return lite_hidden_servers_.count(url) > 0; }
void hideLiteServer(const std::string& url) { lite_hidden_servers_.insert(url); }
void unhideLiteServer(const std::string& url) { lite_hidden_servers_.erase(url); }
// Lite wallet rollout / kill-switch (see wallet/lite_rollout_policy.h).
// Override: "auto" (honor rollout manifest), "force_on", or "force_off".
std::string getLiteRolloutOverride() const { return lite_rollout_override_; }
void setLiteRolloutOverride(const std::string& v) { lite_rollout_override_ = v; }
// Stable, locally-generated install id used only to derive the staged-rollout bucket.
// Never transmitted; carries no PII. Generated on first use if empty.
std::string getLiteInstallId() const { return lite_install_id_; }
void setLiteInstallId(const std::string& v) { lite_install_id_ = v; }
// Verbose diagnostic logging (connection attempts, daemon state, port owner, etc.)
bool getVerboseLogging() const { return verbose_logging_; }
void setVerboseLogging(bool v) { verbose_logging_ = v; }
// Daemon — debug logging categories
const std::set<std::string>& getDebugCategories() const { return debug_categories_; }
void setDebugCategories(const std::set<std::string>& cats) { debug_categories_ = cats; }
bool hasDebugCategory(const std::string& cat) const { return debug_categories_.count(cat) > 0; }
void toggleDebugCategory(const std::string& cat) {
if (debug_categories_.count(cat)) debug_categories_.erase(cat);
else debug_categories_.insert(cat);
}
// Visual effects — animated theme effects (hue cycling, shimmer, glow, etc.)
bool getThemeEffectsEnabled() const { return theme_effects_enabled_; }
void setThemeEffectsEnabled(bool v) { theme_effects_enabled_ = v; }
// Low-spec mode — disables heavy visual effects for better performance
bool getLowSpecMode() const { return low_spec_mode_; }
void setLowSpecMode(bool v) { low_spec_mode_ = v; }
// Reduce motion — disables animated transitions for accessibility
bool getReduceMotion() const { return reduce_motion_; }
void setReduceMotion(bool v) { reduce_motion_ = v; }
// Market — last selected exchange + pair
std::string getSelectedExchange() const { return selected_exchange_; }
void setSelectedExchange(const std::string& v) { selected_exchange_ = v; }
std::string getSelectedPair() const { return selected_pair_; }
void setSelectedPair(const std::string& v) { selected_pair_ = v; }
// Pool mining
std::string getPoolUrl() const { return pool_url_; }
void setPoolUrl(const std::string& v) { pool_url_ = v; }
std::string getPoolAlgo() const { return pool_algo_; }
void setPoolAlgo(const std::string& v) { pool_algo_ = v; }
std::string getPoolWorker() const { return pool_worker_; }
void setPoolWorker(const std::string& v) { pool_worker_ = v; }
int getPoolThreads() const { return pool_threads_; }
void setPoolThreads(int v) { pool_threads_ = v; }
bool getPoolTls() const { return pool_tls_; }
void setPoolTls(bool v) { pool_tls_ = v; }
bool getPoolHugepages() const { return pool_hugepages_; }
void setPoolHugepages(bool v) { pool_hugepages_ = v; }
bool getPoolMode() const { return pool_mode_; }
void setPoolMode(bool v) { pool_mode_ = v; }
// Installed DRG-XMRig release tag (for in-app miner update detection); empty if unknown/bundled.
std::string getXmrigVersion() const { return xmrig_version_; }
void setXmrigVersion(const std::string& v) { xmrig_version_ = v; }
// Mine when idle (auto-start mining when system is idle)
bool getMineWhenIdle() const { return mine_when_idle_; }
void setMineWhenIdle(bool v) { mine_when_idle_ = v; }
int getMineIdleDelay() const { return mine_idle_delay_; }
void setMineIdleDelay(int seconds) { mine_idle_delay_ = std::max(30, seconds); }
// Idle thread scaling — scale thread count instead of start/stop
bool getIdleThreadScaling() const { return idle_thread_scaling_; }
void setIdleThreadScaling(bool v) { idle_thread_scaling_ = v; }
int getIdleThreadsActive() const { return idle_threads_active_; }
void setIdleThreadsActive(int v) { idle_threads_active_ = std::max(0, v); }
int getIdleThreadsIdle() const { return idle_threads_idle_; }
void setIdleThreadsIdle(int v) { idle_threads_idle_ = std::max(0, v); }
bool getIdleGpuAware() const { return idle_gpu_aware_; }
void setIdleGpuAware(bool v) { idle_gpu_aware_ = v; }
// Saved pool URLs (user-managed favorites dropdown)
const std::vector<std::string>& getSavedPoolUrls() const { return saved_pool_urls_; }
void addSavedPoolUrl(const std::string& url) {
// Don't add duplicates
for (const auto& u : saved_pool_urls_) if (u == url) return;
saved_pool_urls_.push_back(url);
}
void removeSavedPoolUrl(const std::string& url) {
saved_pool_urls_.erase(
std::remove(saved_pool_urls_.begin(), saved_pool_urls_.end(), url),
saved_pool_urls_.end());
}
// Saved pool worker addresses (user-managed favorites dropdown)
const std::vector<std::string>& getSavedPoolWorkers() const { return saved_pool_workers_; }
void addSavedPoolWorker(const std::string& addr) {
for (const auto& a : saved_pool_workers_) if (a == addr) return;
saved_pool_workers_.push_back(addr);
}
void removeSavedPoolWorker(const std::string& addr) {
saved_pool_workers_.erase(
std::remove(saved_pool_workers_.begin(), saved_pool_workers_.end(), addr),
saved_pool_workers_.end());
}
// Font scale (user accessibility setting, 1.01.5)
float getFontScale() const { return font_scale_; }
void setFontScale(float v) { font_scale_ = std::max(1.0f, std::min(1.5f, v)); }
// Window size persistence (logical pixels at 1x scale)
int getWindowWidth() const { return window_width_; }
int getWindowHeight() const { return window_height_; }
void setWindowSize(int w, int h) { window_width_ = w; window_height_ = h; }
// Returns true once after an upgrade (version mismatch detected on load)
bool needsUpgradeSave() const { return needs_upgrade_save_; }
void clearUpgradeSave() { needs_upgrade_save_ = false; }
private:
std::string settings_path_;
// Settings values
std::string theme_ = "dragonx";
std::string skin_id_ = "dragonx";
bool save_ztxs_ = true;
bool auto_shield_ = true;
bool use_tor_ = false;
bool allow_custom_fees_ = false;
double default_fee_ = 0.0001;
bool fetch_prices_ = true;
std::string tx_explorer_url_ = "https://explorer.dragonx.is/tx/";
std::string address_explorer_url_ = "https://explorer.dragonx.is/address/";
std::string language_ = "en";
bool acrylic_enabled_ = true;
int acrylic_quality_ = 2;
float blur_multiplier_ = 0.10f;
float noise_opacity_ = 0.5f;
bool gradient_background_ = false;
#ifdef _WIN32
float ui_opacity_ = 0.50f; // Card/sidebar opacity (0.31.0, 1.0 = opaque)
float window_opacity_ = 0.75f; // Background alpha (0.31.0, <1 = desktop visible)
#else
float ui_opacity_ = 1.0f; // Mac/Linux: default fully opaque
float window_opacity_ = 1.0f; // Mac/Linux: default fully opaque
#endif
std::string balance_layout_ = "classic";
bool scanline_enabled_ = true;
std::set<std::string> hidden_addresses_;
std::set<std::string> favorite_addresses_;
std::map<std::string, AddressMeta> address_meta_;
bool wizard_completed_ = false;
int auto_lock_timeout_ = 900; // 15 minutes
int unlock_duration_ = 600; // 10 minutes
bool pin_enabled_ = false;
bool keep_daemon_running_ = false;
bool stop_external_daemon_ = false;
int max_connections_ = 0; // 0 = daemon default
// Lite wallet server preferences. These are user/server settings only;
// wallet secrets, wallet files, and lifecycle state are never stored here.
LiteServerSelectionPreferenceMode lite_server_selection_mode_ = LiteServerSelectionPreferenceMode::Sticky;
std::string lite_sticky_server_url_ = "https://lite.dragonx.is";
std::string lite_chain_name_ = "main"; // SDXL backend chain id; must be main/test/regtest
std::size_t lite_random_selection_seed_ = 0;
bool lite_persist_selected_server_ = true;
std::string lite_rollout_override_ = "auto"; // auto|force_on|force_off
std::string lite_install_id_; // random local-only id; rollout-bucket source
std::vector<LiteServerPreference> lite_servers_ = {
{"https://lite.dragonx.is", "DragonX Lite", true},
{"https://lite1.dragonx.is", "DragonX Lite 1", true},
{"https://lite2.dragonx.is", "DragonX Lite 2", true},
{"https://lite3.dragonx.is", "DragonX Lite 3", true},
{"https://lite4.dragonx.is", "DragonX Lite 4", true},
{"https://lite5.dragonx.is", "DragonX Lite 5", true}
};
std::set<std::string> lite_hidden_servers_; // server URLs hidden from the Network tab
bool verbose_logging_ = false;
std::set<std::string> debug_categories_;
bool theme_effects_enabled_ = true;
bool low_spec_mode_ = false;
bool reduce_motion_ = false;
std::string selected_exchange_ = "TradeOgre";
std::string selected_pair_ = "DRGX/BTC";
// Pool mining
std::string pool_url_ = "pool.dragonx.is:3433";
std::string pool_algo_ = "rx/hush";
std::string pool_worker_ = "";
int pool_threads_ = 0;
bool pool_tls_ = false;
bool pool_hugepages_ = true;
bool pool_mode_ = false; // false=solo, true=pool
std::string xmrig_version_; // installed DRG-XMRig release tag (update detection)
bool mine_when_idle_ = false; // auto-start mining when system idle
int mine_idle_delay_= 120; // seconds of idle before mining starts
bool idle_thread_scaling_ = false; // scale threads instead of start/stop
int idle_threads_active_ = 0; // threads when user active (0 = auto)
int idle_threads_idle_ = 0; // threads when idle (0 = auto = all)
bool idle_gpu_aware_ = true; // treat GPU activity as non-idle
std::vector<std::string> saved_pool_urls_; // user-saved pool URL favorites
std::vector<std::string> saved_pool_workers_; // user-saved worker address favorites
// Font scale (user accessibility, 1.03.0; 1.0 = default)
float font_scale_ = 1.0f;
// Window size (logical pixels at 1x scale; 0 = use default 1200×775)
int window_width_ = 0;
int window_height_ = 0;
// Wallet version that last wrote this settings file (for upgrade detection)
std::string settings_version_;
bool needs_upgrade_save_ = false; // true when version changed
};
} // namespace config
} // namespace dragonx