ObsidianDragon - DragonX ImGui Wallet

Full-node GUI wallet for DragonX cryptocurrency.
Built with Dear ImGui, SDL3, and OpenGL3/DX11.

Features:
- Send/receive shielded and transparent transactions
- Autoshield with merged transaction display
- Built-in CPU mining (xmrig)
- Peer management and network monitoring
- Wallet encryption with PIN lock
- QR code generation for receive addresses
- Transaction history with pagination
- Console for direct RPC commands
- Cross-platform (Linux, Windows)
This commit is contained in:
2026-02-26 02:31:52 -06:00
commit 3aee55b49c
306 changed files with 177789 additions and 0 deletions

242
src/config/settings.cpp Normal file
View File

@@ -0,0 +1,242 @@
// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#include "settings.h"
#include "version.h"
#include <nlohmann/json.hpp>
#include <fstream>
#include <filesystem>
#include "../util/logger.h"
#ifdef _WIN32
#include <shlobj.h>
#else
#include <pwd.h>
#include <unistd.h>
#endif
namespace fs = std::filesystem;
using json = nlohmann::json;
namespace dragonx {
namespace config {
Settings::Settings() = default;
Settings::~Settings() = default;
std::string Settings::getDefaultPath()
{
#ifdef _WIN32
char path[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, path))) {
std::string dir = std::string(path) + "\\ObsidianDragon";
fs::create_directories(dir);
return dir + "\\settings.json";
}
return "settings.json";
#elif defined(__APPLE__)
const char* home = getenv("HOME");
if (!home) {
struct passwd* pw = getpwuid(getuid());
home = pw->pw_dir;
}
std::string dir = std::string(home) + "/Library/Application Support/ObsidianDragon";
fs::create_directories(dir);
return dir + "/settings.json";
#else
const char* home = getenv("HOME");
if (!home) {
struct passwd* pw = getpwuid(getuid());
home = pw->pw_dir;
}
std::string dir = std::string(home) + "/.config/ObsidianDragon";
fs::create_directories(dir);
return dir + "/settings.json";
#endif
}
bool Settings::load()
{
return load(getDefaultPath());
}
bool Settings::load(const std::string& path)
{
settings_path_ = path;
std::ifstream file(path);
if (!file.is_open()) {
return false;
}
try {
json j;
file >> j;
if (j.contains("theme")) theme_ = j["theme"].get<std::string>();
if (j.contains("save_ztxs")) save_ztxs_ = j["save_ztxs"].get<bool>();
if (j.contains("auto_shield")) auto_shield_ = j["auto_shield"].get<bool>();
if (j.contains("use_tor")) use_tor_ = j["use_tor"].get<bool>();
if (j.contains("allow_custom_fees")) allow_custom_fees_ = j["allow_custom_fees"].get<bool>();
if (j.contains("default_fee")) default_fee_ = j["default_fee"].get<double>();
if (j.contains("fetch_prices")) fetch_prices_ = j["fetch_prices"].get<bool>();
if (j.contains("tx_explorer_url")) tx_explorer_url_ = j["tx_explorer_url"].get<std::string>();
if (j.contains("address_explorer_url")) address_explorer_url_ = j["address_explorer_url"].get<std::string>();
if (j.contains("language")) language_ = j["language"].get<std::string>();
if (j.contains("skin_id")) skin_id_ = j["skin_id"].get<std::string>();
if (j.contains("acrylic_enabled")) acrylic_enabled_ = j["acrylic_enabled"].get<bool>();
if (j.contains("acrylic_quality")) acrylic_quality_ = j["acrylic_quality"].get<int>();
if (j.contains("blur_multiplier")) blur_multiplier_ = j["blur_multiplier"].get<float>();
if (j.contains("noise_opacity")) noise_opacity_ = j["noise_opacity"].get<float>();
if (j.contains("gradient_background")) gradient_background_ = j["gradient_background"].get<bool>();
// Migrate legacy reduced_transparency bool -> ui_opacity float
if (j.contains("ui_opacity")) {
ui_opacity_ = j["ui_opacity"].get<float>();
} else if (j.contains("reduced_transparency") && j["reduced_transparency"].get<bool>()) {
ui_opacity_ = 1.0f; // legacy: reduced = fully opaque
}
if (j.contains("window_opacity")) window_opacity_ = j["window_opacity"].get<float>();
if (j.contains("balance_layout")) {
if (j["balance_layout"].is_string())
balance_layout_ = j["balance_layout"].get<std::string>();
else if (j["balance_layout"].is_number_integer()) {
// Legacy migration: convert old int index to string ID
static const char* legacyIds[] = {
"classic","donut","consolidated","dashboard",
"vertical-stack","shield","timeline","two-row","minimal"
};
int idx = j["balance_layout"].get<int>();
if (idx >= 0 && idx < 9) balance_layout_ = legacyIds[idx];
}
}
if (j.contains("scanline_enabled")) scanline_enabled_ = j["scanline_enabled"].get<bool>();
if (j.contains("hidden_addresses") && j["hidden_addresses"].is_array()) {
hidden_addresses_.clear();
for (const auto& a : j["hidden_addresses"])
if (a.is_string()) hidden_addresses_.insert(a.get<std::string>());
}
if (j.contains("favorite_addresses") && j["favorite_addresses"].is_array()) {
favorite_addresses_.clear();
for (const auto& a : j["favorite_addresses"])
if (a.is_string()) favorite_addresses_.insert(a.get<std::string>());
}
if (j.contains("wizard_completed")) wizard_completed_ = j["wizard_completed"].get<bool>();
if (j.contains("auto_lock_timeout")) auto_lock_timeout_ = j["auto_lock_timeout"].get<int>();
if (j.contains("unlock_duration")) unlock_duration_ = j["unlock_duration"].get<int>();
if (j.contains("pin_enabled")) pin_enabled_ = j["pin_enabled"].get<bool>();
if (j.contains("keep_daemon_running")) keep_daemon_running_ = j["keep_daemon_running"].get<bool>();
if (j.contains("stop_external_daemon")) stop_external_daemon_ = j["stop_external_daemon"].get<bool>();
if (j.contains("debug_categories") && j["debug_categories"].is_array()) {
debug_categories_.clear();
for (const auto& c : j["debug_categories"])
if (c.is_string()) debug_categories_.insert(c.get<std::string>());
}
if (j.contains("theme_effects_enabled")) theme_effects_enabled_ = j["theme_effects_enabled"].get<bool>();
if (j.contains("low_spec_mode")) low_spec_mode_ = j["low_spec_mode"].get<bool>();
if (j.contains("selected_exchange")) selected_exchange_ = j["selected_exchange"].get<std::string>();
if (j.contains("selected_pair")) selected_pair_ = j["selected_pair"].get<std::string>();
if (j.contains("pool_url")) pool_url_ = j["pool_url"].get<std::string>();
if (j.contains("pool_algo")) pool_algo_ = j["pool_algo"].get<std::string>();
if (j.contains("pool_worker")) pool_worker_ = j["pool_worker"].get<std::string>();
if (j.contains("pool_threads")) pool_threads_ = j["pool_threads"].get<int>();
if (j.contains("pool_tls")) pool_tls_ = j["pool_tls"].get<bool>();
if (j.contains("pool_hugepages")) pool_hugepages_ = j["pool_hugepages"].get<bool>();
if (j.contains("pool_mode")) pool_mode_ = j["pool_mode"].get<bool>();
if (j.contains("window_width") && j["window_width"].is_number_integer())
window_width_ = j["window_width"].get<int>();
if (j.contains("window_height") && j["window_height"].is_number_integer())
window_height_ = j["window_height"].get<int>();
return true;
} catch (const std::exception& e) {
DEBUG_LOGF("Failed to parse settings: %s\n", e.what());
return false;
}
}
bool Settings::save()
{
if (settings_path_.empty()) {
settings_path_ = getDefaultPath();
}
return save(settings_path_);
}
bool Settings::save(const std::string& path)
{
json j;
j["theme"] = theme_;
j["save_ztxs"] = save_ztxs_;
j["auto_shield"] = auto_shield_;
j["use_tor"] = use_tor_;
j["allow_custom_fees"] = allow_custom_fees_;
j["default_fee"] = default_fee_;
j["fetch_prices"] = fetch_prices_;
j["tx_explorer_url"] = tx_explorer_url_;
j["address_explorer_url"] = address_explorer_url_;
j["language"] = language_;
j["skin_id"] = skin_id_;
j["acrylic_enabled"] = acrylic_enabled_;
j["acrylic_quality"] = acrylic_quality_;
j["blur_multiplier"] = blur_multiplier_;
j["noise_opacity"] = noise_opacity_;
j["gradient_background"] = gradient_background_;
j["ui_opacity"] = ui_opacity_;
j["window_opacity"] = window_opacity_;
j["balance_layout"] = balance_layout_; // saved as string ID
j["scanline_enabled"] = scanline_enabled_;
j["hidden_addresses"] = json::array();
for (const auto& addr : hidden_addresses_)
j["hidden_addresses"].push_back(addr);
j["favorite_addresses"] = json::array();
for (const auto& addr : favorite_addresses_)
j["favorite_addresses"].push_back(addr);
j["wizard_completed"] = wizard_completed_;
j["auto_lock_timeout"] = auto_lock_timeout_;
j["unlock_duration"] = unlock_duration_;
j["pin_enabled"] = pin_enabled_;
j["keep_daemon_running"] = keep_daemon_running_;
j["stop_external_daemon"] = stop_external_daemon_;
j["debug_categories"] = json::array();
for (const auto& cat : debug_categories_)
j["debug_categories"].push_back(cat);
j["theme_effects_enabled"] = theme_effects_enabled_;
j["low_spec_mode"] = low_spec_mode_;
j["selected_exchange"] = selected_exchange_;
j["selected_pair"] = selected_pair_;
j["pool_url"] = pool_url_;
j["pool_algo"] = pool_algo_;
j["pool_worker"] = pool_worker_;
j["pool_threads"] = pool_threads_;
j["pool_tls"] = pool_tls_;
j["pool_hugepages"] = pool_hugepages_;
j["pool_mode"] = pool_mode_;
if (window_width_ > 0 && window_height_ > 0) {
j["window_width"] = window_width_;
j["window_height"] = window_height_;
}
try {
// Ensure directory exists
fs::path p(path);
fs::create_directories(p.parent_path());
std::ofstream file(path);
if (!file.is_open()) {
return false;
}
file << j.dump(4);
return true;
} catch (const std::exception& e) {
DEBUG_LOGF("Failed to save settings: %s\n", e.what());
return false;
}
}
} // namespace config
} // namespace dragonx

263
src/config/settings.h Normal file
View File

@@ -0,0 +1,263 @@
// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#include <string>
#include <set>
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();
// 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(); }
// 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 — 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; }
// 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; }
// 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; }
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;
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)
std::string balance_layout_ = "classic";
bool scanline_enabled_ = true;
std::set<std::string> hidden_addresses_;
std::set<std::string> favorite_addresses_;
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;
std::set<std::string> debug_categories_;
bool theme_effects_enabled_ = true;
bool low_spec_mode_ = false;
std::string selected_exchange_ = "TradeOgre";
std::string selected_pair_ = "DRGX/BTC";
// Pool mining
std::string pool_url_ = "pool.dragonx.is";
std::string pool_algo_ = "rx/hush";
std::string pool_worker_ = "x";
int pool_threads_ = 0;
bool pool_tls_ = false;
bool pool_hugepages_ = true;
bool pool_mode_ = false; // false=solo, true=pool
// Window size (logical pixels at 1x scale; 0 = use default 1200×775)
int window_width_ = 0;
int window_height_ = 0;
};
} // namespace config
} // namespace dragonx

28
src/config/version.h Normal file
View File

@@ -0,0 +1,28 @@
// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#pragma once
#define DRAGONX_VERSION "1.0.0"
#define DRAGONX_VERSION_MAJOR 1
#define DRAGONX_VERSION_MINOR 0
#define DRAGONX_VERSION_PATCH 0
#define DRAGONX_APP_NAME "ObsidianDragon"
#define DRAGONX_ORG_NAME "Hush"
// Default RPC settings
#define DRAGONX_DEFAULT_RPC_HOST "127.0.0.1"
#define DRAGONX_DEFAULT_RPC_PORT "21769"
// Coin parameters
#define DRAGONX_TICKER "DRGX"
#define DRAGONX_COIN_NAME "DragonX"
#define DRAGONX_URI_SCHEME "drgx"
#define DRAGONX_ZATOSHI_PER_COIN 100000000
#define DRAGONX_DEFAULT_FEE 0.0001
// Config file names
#define DRAGONX_CONF_FILENAME "DRAGONX.conf"
#define DRAGONX_WALLET_FILENAME "wallet.dat"