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:
247
src/ui/windows/key_export_dialog.cpp
Normal file
247
src/ui/windows/key_export_dialog.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#include "key_export_dialog.h"
|
||||
#include "../../app.h"
|
||||
#include "../../rpc/rpc_client.h"
|
||||
#include "../../rpc/rpc_worker.h"
|
||||
#include "../../util/i18n.h"
|
||||
#include "../theme.h"
|
||||
#include "../effects/imgui_acrylic.h"
|
||||
#include "../schema/ui_schema.h"
|
||||
#include "../material/draw_helpers.h"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace dragonx {
|
||||
namespace ui {
|
||||
|
||||
// Static member initialization
|
||||
bool KeyExportDialog::s_open = false;
|
||||
bool KeyExportDialog::s_fetching = false;
|
||||
bool KeyExportDialog::s_show_key = false;
|
||||
KeyExportDialog::KeyType KeyExportDialog::s_key_type = KeyExportDialog::KeyType::Private;
|
||||
std::string KeyExportDialog::s_address;
|
||||
std::string KeyExportDialog::s_key;
|
||||
std::string KeyExportDialog::s_error;
|
||||
|
||||
void KeyExportDialog::show(const std::string& address, KeyType type)
|
||||
{
|
||||
s_open = true;
|
||||
s_fetching = false;
|
||||
s_show_key = false;
|
||||
s_key_type = type;
|
||||
s_address = address;
|
||||
s_key.clear();
|
||||
s_error.clear();
|
||||
}
|
||||
|
||||
bool KeyExportDialog::isOpen()
|
||||
{
|
||||
return s_open;
|
||||
}
|
||||
|
||||
void KeyExportDialog::render(App* app)
|
||||
{
|
||||
if (!s_open) return;
|
||||
|
||||
const char* title = (s_key_type == KeyType::Private) ?
|
||||
TR("export_private_key") : TR("export_viewing_key");
|
||||
|
||||
auto& S = schema::UI();
|
||||
auto win = S.window("dialogs.key-export");
|
||||
auto warningBox = S.drawElement("dialogs.key-export", "warning-box");
|
||||
auto addrInput = S.input("dialogs.key-export", "address-input");
|
||||
auto revealBtn = S.button("dialogs.key-export", "reveal-button");
|
||||
auto keyDisplay = S.drawElement("dialogs.key-export", "key-display");
|
||||
auto toggleBtn = S.button("dialogs.key-export", "toggle-button");
|
||||
auto copyBtn = S.button("dialogs.key-export", "copy-button");
|
||||
auto closeBtn = S.button("dialogs.key-export", "close-button");
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(win.width, win.height), ImGuiCond_FirstUseEver);
|
||||
ImGui::OpenPopup(title);
|
||||
|
||||
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
|
||||
const auto& acrylicTheme = GetCurrentAcrylicTheme();
|
||||
if (effects::ImGuiAcrylic::BeginAcrylicPopupModal(title, &s_open,
|
||||
ImGuiWindowFlags_NoResize, acrylicTheme.popup)) {
|
||||
ImGui::Spacing();
|
||||
|
||||
// Warning section with colored background
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.6f, 0.2f, 0.2f, 0.3f));
|
||||
ImGui::BeginChild("WarningBox", ImVec2(-1, warningBox.height > 0 ? warningBox.height : 80), true);
|
||||
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), " WARNING!");
|
||||
ImGui::Spacing();
|
||||
|
||||
if (s_key_type == KeyType::Private) {
|
||||
ImGui::TextWrapped(" Keep this key SECRET! Anyone with this key can spend your "
|
||||
"funds. Never share it online or with untrusted parties.");
|
||||
} else {
|
||||
ImGui::TextWrapped(" This viewing key allows others to see your incoming transactions "
|
||||
"and balance, but NOT spend your funds. Share only with trusted parties.");
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Address display
|
||||
ImGui::Text("Address:");
|
||||
|
||||
// Determine if it's a z-address (longer) or t-address
|
||||
bool is_zaddr = s_address.length() > 50;
|
||||
|
||||
if (is_zaddr) {
|
||||
// Use multiline for z-addresses
|
||||
char addr_buf[512];
|
||||
strncpy(addr_buf, s_address.c_str(), sizeof(addr_buf) - 1);
|
||||
addr_buf[sizeof(addr_buf) - 1] = '\0';
|
||||
ImGui::InputTextMultiline("##Address", addr_buf, sizeof(addr_buf),
|
||||
ImVec2(-1, addrInput.height > 0 ? addrInput.height : 60), ImGuiInputTextFlags_ReadOnly);
|
||||
} else {
|
||||
char addr_buf[128];
|
||||
strncpy(addr_buf, s_address.c_str(), sizeof(addr_buf) - 1);
|
||||
addr_buf[sizeof(addr_buf) - 1] = '\0';
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
ImGui::InputText("##Address", addr_buf, sizeof(addr_buf), ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// Key display section
|
||||
const char* key_label = (s_key_type == KeyType::Private) ? "Private Key:" : "Viewing Key:";
|
||||
ImGui::Text("%s", key_label);
|
||||
|
||||
if (s_fetching) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Fetching key from wallet...");
|
||||
} else if (!s_error.empty()) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Error: %s", s_error.c_str());
|
||||
} else if (s_key.empty()) {
|
||||
// Show button to fetch key
|
||||
if (material::StyledButton("Reveal Key", ImVec2(revealBtn.width, 0), S.resolveFont(revealBtn.font))) {
|
||||
s_fetching = true;
|
||||
|
||||
// Check if z-address or t-address
|
||||
bool is_zaddress = (s_address.length() > 50 || s_address[0] == 'z');
|
||||
|
||||
if (s_key_type == KeyType::Private) {
|
||||
// Export private key
|
||||
std::string addr = s_address;
|
||||
std::string method = is_zaddress ? "z_exportkey" : "dumpprivkey";
|
||||
if (app->worker()) {
|
||||
app->worker()->post([rpc = app->rpc(), addr, method]() -> rpc::RPCWorker::MainCb {
|
||||
std::string key;
|
||||
std::string error;
|
||||
try {
|
||||
auto result = rpc->call(method, {addr});
|
||||
key = result.get<std::string>();
|
||||
} catch (const std::exception& e) {
|
||||
error = e.what();
|
||||
}
|
||||
return [key, error]() {
|
||||
if (error.empty()) {
|
||||
s_key = key;
|
||||
s_show_key = false; // Don't show by default
|
||||
} else {
|
||||
s_error = error;
|
||||
}
|
||||
s_fetching = false;
|
||||
};
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Export viewing key (only for z-addresses)
|
||||
if (is_zaddress) {
|
||||
std::string addr = s_address;
|
||||
if (app->worker()) {
|
||||
app->worker()->post([rpc = app->rpc(), addr]() -> rpc::RPCWorker::MainCb {
|
||||
std::string key;
|
||||
std::string error;
|
||||
try {
|
||||
auto result = rpc->call("z_exportviewingkey", {addr});
|
||||
key = result.get<std::string>();
|
||||
} catch (const std::exception& e) {
|
||||
error = e.what();
|
||||
}
|
||||
return [key, error]() {
|
||||
if (error.empty()) {
|
||||
s_key = key;
|
||||
s_show_key = true; // Viewing keys are less sensitive
|
||||
} else {
|
||||
s_error = error;
|
||||
}
|
||||
s_fetching = false;
|
||||
};
|
||||
});
|
||||
}
|
||||
} else {
|
||||
s_error = "Viewing keys are only available for shielded (z) addresses";
|
||||
s_fetching = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("Click to retrieve the key from your wallet");
|
||||
} else {
|
||||
// Key has been fetched - display it
|
||||
|
||||
if (s_show_key) {
|
||||
// Show the actual key
|
||||
char key_buf[1024];
|
||||
strncpy(key_buf, s_key.c_str(), sizeof(key_buf) - 1);
|
||||
key_buf[sizeof(key_buf) - 1] = '\0';
|
||||
ImGui::InputTextMultiline("##Key", key_buf, sizeof(key_buf),
|
||||
ImVec2(-1, keyDisplay.height > 0 ? keyDisplay.height : 80), ImGuiInputTextFlags_ReadOnly);
|
||||
} else {
|
||||
// Show masked
|
||||
std::string masked(s_key.length(), '*');
|
||||
char masked_buf[1024];
|
||||
strncpy(masked_buf, masked.c_str(), sizeof(masked_buf) - 1);
|
||||
masked_buf[sizeof(masked_buf) - 1] = '\0';
|
||||
ImGui::InputTextMultiline("##Key", masked_buf, sizeof(masked_buf),
|
||||
ImVec2(-1, keyDisplay.height > 0 ? keyDisplay.height : 80), ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
|
||||
// Show/Hide and Copy buttons
|
||||
ImGui::Spacing();
|
||||
|
||||
if (material::StyledButton(s_show_key ? "Hide" : "Show", ImVec2(toggleBtn.width, 0), S.resolveFont(toggleBtn.font))) {
|
||||
s_show_key = !s_show_key;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (material::StyledButton(TR("copy_to_clipboard"), ImVec2(copyBtn.width, 0), S.resolveFont(copyBtn.font))) {
|
||||
ImGui::SetClipboardText(s_key.c_str());
|
||||
// Could add a notification here
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// Close button
|
||||
float button_width = closeBtn.width;
|
||||
float avail_width = ImGui::GetContentRegionAvail().x;
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (avail_width - button_width) / 2.0f);
|
||||
|
||||
if (material::StyledButton("Close", ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) {
|
||||
s_open = false;
|
||||
// Clear sensitive data
|
||||
s_key.clear();
|
||||
s_show_key = false;
|
||||
}
|
||||
|
||||
effects::ImGuiAcrylic::EndAcrylicPopup();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
Reference in New Issue
Block a user