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)
200 lines
7.1 KiB
C++
200 lines
7.1 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
|
|
#include "backup_wallet_dialog.h"
|
|
#include "../../app.h"
|
|
#include "../../rpc/rpc_client.h"
|
|
#include "../../rpc/rpc_worker.h"
|
|
#include "../../util/i18n.h"
|
|
#include "../../util/platform.h"
|
|
#include "../notifications.h"
|
|
#include "../schema/ui_schema.h"
|
|
#include "../material/draw_helpers.h"
|
|
#include "../theme.h"
|
|
#include "../effects/imgui_acrylic.h"
|
|
#include "imgui.h"
|
|
|
|
#include <string>
|
|
#include <ctime>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
namespace fs = std::filesystem;
|
|
using json = nlohmann::json;
|
|
|
|
// Static state
|
|
static bool s_open = false;
|
|
static char s_destination[512] = "";
|
|
static std::string s_status;
|
|
static bool s_backing_up = false;
|
|
|
|
void BackupWalletDialog::show()
|
|
{
|
|
s_open = true;
|
|
s_status.clear();
|
|
s_backing_up = false;
|
|
|
|
// Generate default destination with timestamp
|
|
std::time_t now = std::time(nullptr);
|
|
char timebuf[32];
|
|
std::strftime(timebuf, sizeof(timebuf), "%Y%m%d_%H%M%S", std::localtime(&now));
|
|
|
|
// Default to home directory
|
|
std::string home = util::Platform::getHomeDir();
|
|
snprintf(s_destination, sizeof(s_destination), "%s/wallet_backup_%s.dat", home.c_str(), timebuf);
|
|
}
|
|
|
|
bool BackupWalletDialog::isOpen()
|
|
{
|
|
return s_open;
|
|
}
|
|
|
|
void BackupWalletDialog::render(App* app)
|
|
{
|
|
if (!s_open) return;
|
|
|
|
auto& S = schema::UI();
|
|
auto win = S.window("dialogs.backup-wallet");
|
|
auto backupBtn = S.button("dialogs.backup-wallet", "backup-button");
|
|
auto closeBtn = S.button("dialogs.backup-wallet", "close-button");
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(win.width, win.height), ImGuiCond_FirstUseEver);
|
|
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
|
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
|
ImGui::SetNextWindowFocus();
|
|
|
|
const auto& acrylicTheme = GetCurrentAcrylicTheme();
|
|
ImGui::OpenPopup("Backup Wallet");
|
|
if (effects::ImGuiAcrylic::BeginAcrylicPopupModal("Backup Wallet", &s_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar, acrylicTheme.popup)) {
|
|
ImGui::TextWrapped(
|
|
"Create a backup of your wallet.dat file. This file contains all your "
|
|
"private keys and transaction history. Store the backup in a secure location."
|
|
);
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
if (s_backing_up) {
|
|
ImGui::BeginDisabled();
|
|
}
|
|
|
|
// Destination path
|
|
ImGui::Text("Backup destination:");
|
|
ImGui::SetNextItemWidth(-1);
|
|
ImGui::InputText("##Destination", s_destination, sizeof(s_destination));
|
|
|
|
ImGui::Spacing();
|
|
|
|
// Show wallet.dat location
|
|
std::string walletPath = util::Platform::getDataDir() + "/wallet.dat";
|
|
ImGui::TextDisabled("Source: %s", walletPath.c_str());
|
|
|
|
// Check if source exists
|
|
bool sourceExists = fs::exists(walletPath);
|
|
if (!sourceExists) {
|
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f));
|
|
ImGui::Text("Warning: wallet.dat not found at expected location");
|
|
ImGui::PopStyleColor();
|
|
}
|
|
|
|
if (s_backing_up) {
|
|
ImGui::EndDisabled();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
// Backup button - use RPC backupwallet
|
|
if (s_backing_up) {
|
|
ImGui::BeginDisabled();
|
|
}
|
|
|
|
if (material::StyledButton("Create Backup", ImVec2(backupBtn.width, 0), S.resolveFont(backupBtn.font))) {
|
|
if (strlen(s_destination) == 0) {
|
|
Notifications::instance().warning("Please enter a destination path");
|
|
} else if (!app->rpc() || !app->rpc()->isConnected()) {
|
|
Notifications::instance().error("Not connected to daemon");
|
|
} else {
|
|
s_backing_up = true;
|
|
s_status = "Creating backup...";
|
|
|
|
// Run backup on worker thread to avoid freezing UI
|
|
std::string dest(s_destination);
|
|
if (app->worker()) {
|
|
app->worker()->post([rpc = app->rpc(), dest]() -> rpc::RPCWorker::MainCb {
|
|
bool success = false;
|
|
std::string statusMsg;
|
|
try {
|
|
rpc->call("backupwallet", json::array({dest}));
|
|
// Check if file was created
|
|
if (fs::exists(dest)) {
|
|
auto size = fs::file_size(dest);
|
|
char sizebuf[32];
|
|
if (size > 1024 * 1024) {
|
|
snprintf(sizebuf, sizeof(sizebuf), "%.2f MB", size / (1024.0 * 1024.0));
|
|
} else if (size > 1024) {
|
|
snprintf(sizebuf, sizeof(sizebuf), "%.2f KB", size / 1024.0);
|
|
} else {
|
|
snprintf(sizebuf, sizeof(sizebuf), "%zu bytes", size);
|
|
}
|
|
statusMsg = std::string("Backup created successfully (") + sizebuf + ")";
|
|
success = true;
|
|
} else {
|
|
statusMsg = "Backup may have failed - file not found";
|
|
}
|
|
} catch (const std::exception& e) {
|
|
statusMsg = std::string("Backup failed: ") + e.what();
|
|
}
|
|
return [success, statusMsg]() {
|
|
s_status = statusMsg;
|
|
s_backing_up = false;
|
|
if (success) {
|
|
Notifications::instance().success("Wallet backup created");
|
|
} else {
|
|
Notifications::instance().warning(statusMsg);
|
|
}
|
|
};
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s_backing_up) {
|
|
ImGui::EndDisabled();
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("Backing up...");
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (material::StyledButton("Close", ImVec2(closeBtn.width, 0), S.resolveFont(closeBtn.font))) {
|
|
s_open = false;
|
|
}
|
|
|
|
// Status
|
|
if (!s_status.empty()) {
|
|
ImGui::Spacing();
|
|
ImGui::TextWrapped("%s", s_status.c_str());
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
// Tips
|
|
ImGui::TextDisabled("Tips:");
|
|
ImGui::BulletText("Store backups on external drives or cloud storage");
|
|
ImGui::BulletText("Create multiple backups in different locations");
|
|
ImGui::BulletText("Test restoring from backup periodically");
|
|
}
|
|
effects::ImGuiAcrylic::EndAcrylicPopup();
|
|
}
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|