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

224
src/rpc/connection.cpp Normal file
View File

@@ -0,0 +1,224 @@
// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#include "connection.h"
#include "../config/version.h"
#include "../resources/embedded_resources.h"
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <ctime>
#include <filesystem>
#include "../util/logger.h"
#ifdef _WIN32
#include <shlobj.h>
#else
#include <pwd.h>
#include <unistd.h>
#endif
namespace fs = std::filesystem;
namespace dragonx {
namespace rpc {
Connection::Connection() = default;
Connection::~Connection() = default;
std::string Connection::getDefaultDataDir()
{
#ifdef _WIN32
char path[MAX_PATH];
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, path))) {
return std::string(path) + "\\Hush\\DRAGONX";
}
return "";
#elif defined(__APPLE__)
const char* home = getenv("HOME");
if (!home) {
struct passwd* pw = getpwuid(getuid());
home = pw->pw_dir;
}
// Match SilentDragonX path: Library/Application Support/Hush/DRAGONX
return std::string(home) + "/Library/Application Support/Hush/DRAGONX";
#else
const char* home = getenv("HOME");
if (!home) {
struct passwd* pw = getpwuid(getuid());
home = pw->pw_dir;
}
return std::string(home) + "/.hush/DRAGONX";
#endif
}
std::string Connection::getDefaultConfPath()
{
return getDefaultDataDir() + "/" + DRAGONX_CONF_FILENAME;
}
std::string Connection::getSaplingParamsDir()
{
// Sapling params are now extracted alongside the daemon binaries
// in <ObsidianDragonDir>/hush3/ — no longer in the legacy ZcashParams dir.
return resources::getDaemonDirectory();
}
bool Connection::verifySaplingParams()
{
std::string params_dir = getSaplingParamsDir();
if (params_dir.empty()) {
DEBUG_LOGF("verifySaplingParams: params dir is empty\n");
return false;
}
#ifdef _WIN32
std::string spend_path = params_dir + "\\sapling-spend.params";
std::string output_path = params_dir + "\\sapling-output.params";
#else
std::string spend_path = params_dir + "/sapling-spend.params";
std::string output_path = params_dir + "/sapling-output.params";
#endif
bool spend_exists = fs::exists(spend_path);
bool output_exists = fs::exists(output_path);
DEBUG_LOGF("verifySaplingParams: dir=%s\n", params_dir.c_str());
DEBUG_LOGF(" spend: %s -> %s\n", spend_path.c_str(), spend_exists ? "found" : "MISSING");
DEBUG_LOGF(" output: %s -> %s\n", output_path.c_str(), output_exists ? "found" : "MISSING");
return spend_exists && output_exists;
}
ConnectionConfig Connection::parseConfFile(const std::string& path)
{
ConnectionConfig config;
std::ifstream file(path);
if (!file.is_open()) {
return config;
}
std::string line;
while (std::getline(file, line)) {
// Skip empty lines and comments
if (line.empty() || line[0] == '#') {
continue;
}
// Parse key=value
size_t eq_pos = line.find('=');
if (eq_pos == std::string::npos) {
continue;
}
std::string key = line.substr(0, eq_pos);
std::string value = line.substr(eq_pos + 1);
// Trim whitespace
while (!key.empty() && (key.back() == ' ' || key.back() == '\t')) {
key.pop_back();
}
while (!value.empty() && (value[0] == ' ' || value[0] == '\t')) {
value.erase(0, 1);
}
// Map to config
if (key == "rpcuser") {
config.rpcuser = value;
} else if (key == "rpcpassword") {
config.rpcpassword = value;
} else if (key == "rpcport") {
config.port = value;
} else if (key == "rpchost" || key == "rpcconnect") {
config.host = value;
} else if (key == "proxy") {
config.proxy = value;
}
}
return config;
}
ConnectionConfig Connection::autoDetectConfig()
{
ConnectionConfig config;
// Ensure data directory exists
std::string data_dir = getDefaultDataDir();
if (!fs::exists(data_dir)) {
DEBUG_LOGF("Creating data directory: %s\n", data_dir.c_str());
fs::create_directories(data_dir);
}
// Try to find DRAGONX.conf
std::string conf_path = getDefaultConfPath();
if (fs::exists(conf_path)) {
config = parseConfFile(conf_path);
config.hush_dir = data_dir;
} else {
// Create a default config file
if (createDefaultConfig(conf_path)) {
config = parseConfFile(conf_path);
config.hush_dir = data_dir;
}
}
// Set defaults for missing values
if (config.host.empty()) {
config.host = DRAGONX_DEFAULT_RPC_HOST;
}
if (config.port.empty()) {
config.port = DRAGONX_DEFAULT_RPC_PORT;
}
return config;
}
bool Connection::createDefaultConfig(const std::string& path)
{
// Generate random rpcuser/rpcpassword
auto generateRandomString = [](int length) -> std::string {
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
std::string result;
result.reserve(length);
std::srand(static_cast<unsigned>(std::time(nullptr)));
for (int i = 0; i < length; i++) {
result += charset[std::rand() % (sizeof(charset) - 1)];
}
return result;
};
std::string rpcuser = generateRandomString(16);
std::string rpcpassword = generateRandomString(32);
std::ofstream file(path);
if (!file.is_open()) {
DEBUG_LOGF("Failed to create config file: %s\n", path.c_str());
return false;
}
file << "# DragonX configuration file\n";
file << "# Auto-generated by DragonX Wallet\n";
file << "\n";
file << "rpcuser=" << rpcuser << "\n";
file << "rpcpassword=" << rpcpassword << "\n";
file << "rpcport=" << DRAGONX_DEFAULT_RPC_PORT << "\n";
file << "server=1\n";
file << "txindex=1\n";
file << "addnode=195.201.20.230\n";
file << "addnode=195.201.137.219\n";
file.close();
DEBUG_LOGF("Created default config file: %s\n", path.c_str());
return true;
}
} // namespace rpc
} // namespace dragonx