feat(recovery): import keys from a legacy wallet backup file (z_importwallet)
Phase 3: for a wallet.dat the current daemon can't open, recover its keys from the OLD wallet's export file. Settings → "Import From Backup File…" opens a dialog that takes the path to a z_exportwallet / dumpwallet dump (or SilentDragon's "Export all private keys") and calls z_importwallet — the daemon reads every key and does ONE rescan, far faster and simpler than importing keys one at a time. On success it nudges the user to shield any mined coinbase from the Shield screen. - New App::importWalletFile(path, cb): runs z_importwallet on the long-call lane (no client timeout — the rescan can take a long time), then refreshAddresses + markWalletMutated. Full-node only. - Only the PATH touches GUI memory, never the keys themselves (the daemon reads the file) — less secret exposure than a pasted-key field. - New renderImportWalletFileDialog (typed path + Paste/Clear, progress, result) + a Settings card. Reuses the existing long-call + z_importwallet infra; the careful single-key import dialog is untouched. No send/sweep/encrypt call changed. Build + ctest green; the import/rescan itself is a daemon RPC, not pure-testable, and wasn't exercised end-to-end here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+84
-1
@@ -2582,7 +2582,11 @@ void App::render()
|
||||
if (show_import_key_) {
|
||||
renderImportKeyDialog();
|
||||
}
|
||||
|
||||
|
||||
if (show_import_wallet_file_) {
|
||||
renderImportWalletFileDialog();
|
||||
}
|
||||
|
||||
if (show_export_key_) {
|
||||
renderExportKeyDialog();
|
||||
}
|
||||
@@ -3949,6 +3953,85 @@ static void RenderScanHeightControl(char* buf, size_t bufSize, int tipHeight, bo
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
void App::renderImportWalletFileDialog()
|
||||
{
|
||||
namespace m = ui::material;
|
||||
float dp = ui::Layout::dpiScale();
|
||||
|
||||
m::OverlayDialogSpec ov;
|
||||
ov.title = TR("import_wallet_file_title");
|
||||
ov.p_open = &show_import_wallet_file_;
|
||||
ov.style = m::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = 640.0f;
|
||||
ov.idSuffix = "importwalletfile";
|
||||
if (!m::BeginOverlayDialog(ov)) return;
|
||||
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
import_wallet_file_done_ = false;
|
||||
import_wallet_file_ok_ = false;
|
||||
import_wallet_file_status_.clear();
|
||||
}
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape) && !import_wallet_file_busy_) show_import_wallet_file_ = false;
|
||||
|
||||
m::DialogWarningHeader(TR("import_wallet_file_note"));
|
||||
ImGui::Spacing();
|
||||
if (!state_.connected) {
|
||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(m::OnSurfaceMedium()), "%s", TR("import_key_need_node"));
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
ImGui::TextUnformatted(TR("import_wallet_file_path_label"));
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::BeginDisabled(import_wallet_file_busy_);
|
||||
ImGui::InputText("##importwalletpath", import_wallet_file_path_, sizeof(import_wallet_file_path_));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (!import_wallet_file_busy_) {
|
||||
if (m::TactileButton(TR("paste"), ImVec2(104 * dp, 0))) {
|
||||
const char* clip = ImGui::GetClipboardText();
|
||||
if (clip) {
|
||||
std::string t = dragonx::util::trimmedClipboardText(clip);
|
||||
snprintf(import_wallet_file_path_, sizeof(import_wallet_file_path_), "%s", t.c_str());
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (m::TactileButton(TR("clear"), ImVec2(104 * dp, 0))) import_wallet_file_path_[0] = '\0';
|
||||
}
|
||||
ImGui::Spacing();
|
||||
|
||||
if (import_wallet_file_busy_) {
|
||||
ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(m::OnSurfaceMedium()), "%s%s",
|
||||
TR("import_wallet_file_importing"), m::LoadingDots());
|
||||
ImGui::Spacing();
|
||||
} else if (import_wallet_file_done_) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(
|
||||
import_wallet_file_ok_ ? m::Success() : m::ReadableError()));
|
||||
ImGui::TextWrapped("%s", import_wallet_file_status_.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(import_wallet_file_busy_ || !state_.connected || import_wallet_file_path_[0] == '\0');
|
||||
if (m::TactileButton(TR("import_wallet_file_import_btn"), ImVec2(190 * dp, 0))) {
|
||||
import_wallet_file_busy_ = true;
|
||||
import_wallet_file_done_ = false;
|
||||
import_wallet_file_status_.clear();
|
||||
importWalletFile(import_wallet_file_path_, [this](bool ok, const std::string& err) {
|
||||
import_wallet_file_busy_ = false;
|
||||
import_wallet_file_done_ = true;
|
||||
import_wallet_file_ok_ = ok;
|
||||
import_wallet_file_status_ = ok ? std::string(TR("import_wallet_file_ok")) : err;
|
||||
});
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(import_wallet_file_busy_);
|
||||
if (m::TactileButton(TR("close"), ImVec2(110 * dp, 0))) show_import_wallet_file_ = false;
|
||||
ImGui::EndDisabled();
|
||||
|
||||
m::EndOverlayDialog();
|
||||
}
|
||||
|
||||
void App::renderImportKeyDialog()
|
||||
{
|
||||
namespace m = ui::material;
|
||||
|
||||
Reference in New Issue
Block a user