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)
185 lines
4.5 KiB
C++
185 lines
4.5 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
|
|
#include "address_book.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 data {
|
|
|
|
AddressBook::AddressBook() = default;
|
|
AddressBook::~AddressBook() = default;
|
|
|
|
std::string AddressBook::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 + "\\addressbook.json";
|
|
}
|
|
return "addressbook.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 + "/addressbook.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 + "/addressbook.json";
|
|
#endif
|
|
}
|
|
|
|
bool AddressBook::load()
|
|
{
|
|
file_path_ = getDefaultPath();
|
|
|
|
std::ifstream file(file_path_);
|
|
if (!file.is_open()) {
|
|
// No file yet - that's OK
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
json j;
|
|
file >> j;
|
|
|
|
entries_.clear();
|
|
|
|
if (j.contains("entries") && j["entries"].is_array()) {
|
|
for (const auto& entry : j["entries"]) {
|
|
AddressBookEntry e;
|
|
e.label = entry.value("label", "");
|
|
e.address = entry.value("address", "");
|
|
e.notes = entry.value("notes", "");
|
|
|
|
if (!e.address.empty()) {
|
|
entries_.push_back(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
DEBUG_LOGF("Address book loaded: %zu entries\n", entries_.size());
|
|
return true;
|
|
|
|
} catch (const std::exception& e) {
|
|
DEBUG_LOGF("Error loading address book: %s\n", e.what());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool AddressBook::save()
|
|
{
|
|
if (file_path_.empty()) {
|
|
file_path_ = getDefaultPath();
|
|
}
|
|
|
|
try {
|
|
json j;
|
|
j["entries"] = json::array();
|
|
|
|
for (const auto& entry : entries_) {
|
|
json e;
|
|
e["label"] = entry.label;
|
|
e["address"] = entry.address;
|
|
e["notes"] = entry.notes;
|
|
j["entries"].push_back(e);
|
|
}
|
|
|
|
// Ensure directory exists
|
|
fs::path p(file_path_);
|
|
fs::create_directories(p.parent_path());
|
|
|
|
std::ofstream file(file_path_);
|
|
if (!file.is_open()) {
|
|
DEBUG_LOGF("Could not open address book for writing: %s\n", file_path_.c_str());
|
|
return false;
|
|
}
|
|
|
|
file << j.dump(2);
|
|
DEBUG_LOGF("Address book saved: %zu entries\n", entries_.size());
|
|
return true;
|
|
|
|
} catch (const std::exception& e) {
|
|
DEBUG_LOGF("Error saving address book: %s\n", e.what());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool AddressBook::addEntry(const AddressBookEntry& entry)
|
|
{
|
|
// Check for duplicate address
|
|
if (findByAddress(entry.address) >= 0) {
|
|
return false;
|
|
}
|
|
|
|
entries_.push_back(entry);
|
|
return save();
|
|
}
|
|
|
|
bool AddressBook::updateEntry(size_t index, const AddressBookEntry& entry)
|
|
{
|
|
if (index >= entries_.size()) {
|
|
return false;
|
|
}
|
|
|
|
// Check for duplicate address (excluding current entry)
|
|
int existing = findByAddress(entry.address);
|
|
if (existing >= 0 && static_cast<size_t>(existing) != index) {
|
|
return false;
|
|
}
|
|
|
|
entries_[index] = entry;
|
|
return save();
|
|
}
|
|
|
|
bool AddressBook::removeEntry(size_t index)
|
|
{
|
|
if (index >= entries_.size()) {
|
|
return false;
|
|
}
|
|
|
|
entries_.erase(entries_.begin() + index);
|
|
return save();
|
|
}
|
|
|
|
int AddressBook::findByAddress(const std::string& address) const
|
|
{
|
|
for (size_t i = 0; i < entries_.size(); i++) {
|
|
if (entries_[i].address == address) {
|
|
return static_cast<int>(i);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
} // namespace data
|
|
} // namespace dragonx
|