feat(settings): redesign the remove-encryption (decrypt) modal onto reference design

Migrate App::renderDecryptWalletDialog ("Remove Wallet Encryption") onto the
settings-modal reference design — presentation-only for a fund-critical dialog,
plus one deliberate secret-hygiene addition:

- struct-form BeginOverlayDialog(OverlayDialogSpec{BlurFloat, cardWidth 480,
  idSuffix "decrypt"}) replacing the legacy positional overload
- Phase 0 amber TextWrapped warning -> material::DialogWarningHeader(decrypt_warning)
- drop the redundant Phase 0 Separator
- every rendered-chrome string across all 4 phases (PassphraseEntry / Working /
  Success / Error) -> TR() (14 new decrypt_*/try_again keys added to the English
  source + all 8 res/lang/*.json additively; the button reuses
  settings_remove_encryption, the label reuses change_pass_current, cancel/close reused)
- HARDENING: wipe decrypt_pass_buf_ on BlurFloat dismiss (X/Esc/outside-click),
  mirroring the change-passphrase fix; canClose() gates pOpen so this cannot fire
  during the Working phase

The fund-moving async worker pyramid (unlock -> export -> stop -> backup ->
restart -> re-import, vault/PIN cleanup) is byte-for-byte unchanged; the three
in-worker toast notifications and the "Incorrect passphrase" status are left in
English to avoid touching the fund path (a separate service-layer i18n pass).

Verified: build + ctest + hygiene clean; i18n complete in 8 langs with no format
specifiers and the wallet.dat.encrypted.bak literal preserved; a 3-lens adversarial
review (secret-hygiene / fund-path-untouched / ui-i18n) returned zero findings, with
the fund-path lens proving the async pyramid diffs IDENTICAL against HEAD.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-14 12:02:11 -05:00
parent 5985f05fb2
commit 90df19b8b6
10 changed files with 158 additions and 31 deletions

View File

@@ -333,6 +333,19 @@
"date": "Datum", "date": "Datum",
"date_label": "Datum:", "date_label": "Datum:",
"debug_logging": "FEHLERPROTOKOLLIERUNG", "debug_logging": "FEHLERPROTOKOLLIERUNG",
"decrypt_desc": "Die Wallet wird exportiert, der Daemon mit einer frischen, unverschlüsselten Wallet neu gestartet und alle Schlüssel werden erneut importiert. Je nach Wallet-Größe kann dies einige Minuten dauern.",
"decrypt_error_title": "Entschlüsselung fehlgeschlagen",
"decrypt_step_backup": "Verschlüsselte Wallet wird gesichert",
"decrypt_step_export": "Wallet-Schlüssel werden exportiert",
"decrypt_step_restart": "Daemon wird neu gestartet",
"decrypt_step_stop": "Daemon wird gestoppt",
"decrypt_step_unlock": "Wallet wird entsperrt",
"decrypt_success_desc": "Ihre Wallet ist jetzt unverschlüsselt. Eine Sicherung der verschlüsselten Wallet wurde als wallet.dat.encrypted.bak in Ihrem Datenverzeichnis gespeichert.",
"decrypt_success_title": "Wallet erfolgreich entschlüsselt!",
"decrypt_title": "Wallet-Verschlüsselung entfernen",
"decrypt_wait_general": "Bitte warten. Der Daemon exportiert Schlüssel, startet neu und importiert erneut. Dies kann einige Minuten dauern.",
"decrypt_wait_restart": "Warten, bis der Daemon vollständig gestartet ist...",
"decrypt_warning": "Dadurch wird die Verschlüsselung Ihrer Wallet entfernt. Ihre privaten Schlüssel werden ungeschützt auf der Festplatte gespeichert.",
"delete": "Löschen", "delete": "Löschen",
"delete_blockchain": "Blockchain löschen", "delete_blockchain": "Blockchain löschen",
"delete_blockchain_confirm": "Löschen & Neu synchronisieren", "delete_blockchain_confirm": "Löschen & Neu synchronisieren",
@@ -1269,6 +1282,7 @@
"transfer_to": "Überweisen an:", "transfer_to": "Überweisen an:",
"transparent": "Transparent", "transparent": "Transparent",
"transparent_address": "Transparente Adresse", "transparent_address": "Transparente Adresse",
"try_again": "Erneut versuchen",
"tt_addr_url": "Basis-URL zum Anzeigen von Adressen in einem Block-Explorer", "tt_addr_url": "Basis-URL zum Anzeigen von Adressen in einem Block-Explorer",
"tt_address_book": "Gespeicherte Adressen für schnelles Senden verwalten", "tt_address_book": "Gespeicherte Adressen für schnelles Senden verwalten",
"tt_auto_lock": "Wallet nach dieser Inaktivitätszeit sperren", "tt_auto_lock": "Wallet nach dieser Inaktivitätszeit sperren",

View File

@@ -333,6 +333,19 @@
"date": "Fecha", "date": "Fecha",
"date_label": "Fecha:", "date_label": "Fecha:",
"debug_logging": "REGISTRO DE DEPURACIÓN", "debug_logging": "REGISTRO DE DEPURACIÓN",
"decrypt_desc": "Se exportará el monedero, se reiniciará el daemon con un monedero nuevo sin cifrar y se reimportarán todas las claves. Esto puede tardar varios minutos según el tamaño del monedero.",
"decrypt_error_title": "Error al descifrar",
"decrypt_step_backup": "Respaldando el monedero cifrado",
"decrypt_step_export": "Exportando las claves del monedero",
"decrypt_step_restart": "Reiniciando el daemon",
"decrypt_step_stop": "Deteniendo el daemon",
"decrypt_step_unlock": "Desbloqueando el monedero",
"decrypt_success_desc": "Tu monedero ahora está sin cifrar. Se guardó una copia de seguridad del monedero cifrado como wallet.dat.encrypted.bak en tu directorio de datos.",
"decrypt_success_title": "¡Monedero descifrado correctamente!",
"decrypt_title": "Quitar el cifrado del monedero",
"decrypt_wait_general": "Espera. El daemon está exportando claves, reiniciando y reimportando. Esto puede tardar varios minutos.",
"decrypt_wait_restart": "Esperando a que el daemon termine de iniciarse...",
"decrypt_warning": "Esto quitará el cifrado de tu monedero. Tus claves privadas se almacenarán sin protección en el disco.",
"delete": "Eliminar", "delete": "Eliminar",
"delete_blockchain": "Eliminar Blockchain", "delete_blockchain": "Eliminar Blockchain",
"delete_blockchain_confirm": "Eliminar y Resincronizar", "delete_blockchain_confirm": "Eliminar y Resincronizar",
@@ -1269,6 +1282,7 @@
"transfer_to": "Transferir a:", "transfer_to": "Transferir a:",
"transparent": "Transparente", "transparent": "Transparente",
"transparent_address": "Dirección Transparente", "transparent_address": "Dirección Transparente",
"try_again": "Reintentar",
"tt_addr_url": "URL base para ver direcciones en un explorador de bloques", "tt_addr_url": "URL base para ver direcciones en un explorador de bloques",
"tt_address_book": "Administrar direcciones guardadas para envío rápido", "tt_address_book": "Administrar direcciones guardadas para envío rápido",
"tt_auto_lock": "Bloquear billetera después de este tiempo de inactividad", "tt_auto_lock": "Bloquear billetera después de este tiempo de inactividad",

View File

@@ -333,6 +333,19 @@
"date": "Date", "date": "Date",
"date_label": "Date :", "date_label": "Date :",
"debug_logging": "JOURNALISATION DE DÉBOGAGE", "debug_logging": "JOURNALISATION DE DÉBOGAGE",
"decrypt_desc": "Le portefeuille sera exporté, le daemon redémarré avec un nouveau portefeuille non chiffré, et toutes les clés réimportées. Cela peut prendre plusieurs minutes selon la taille du portefeuille.",
"decrypt_error_title": "Échec du déchiffrement",
"decrypt_step_backup": "Sauvegarde du portefeuille chiffré",
"decrypt_step_export": "Exportation des clés du portefeuille",
"decrypt_step_restart": "Redémarrage du daemon",
"decrypt_step_stop": "Arrêt du daemon",
"decrypt_step_unlock": "Déverrouillage du portefeuille",
"decrypt_success_desc": "Votre portefeuille est maintenant non chiffré. Une sauvegarde du portefeuille chiffré a été enregistrée sous wallet.dat.encrypted.bak dans votre répertoire de données.",
"decrypt_success_title": "Portefeuille déchiffré avec succès !",
"decrypt_title": "Supprimer le chiffrement du portefeuille",
"decrypt_wait_general": "Veuillez patienter. Le daemon exporte les clés, redémarre et réimporte. Cela peut prendre plusieurs minutes.",
"decrypt_wait_restart": "En attente du démarrage complet du daemon...",
"decrypt_warning": "Cela supprimera le chiffrement de votre portefeuille. Vos clés privées seront stockées sans protection sur le disque.",
"delete": "Supprimer", "delete": "Supprimer",
"delete_blockchain": "Supprimer Blockchain", "delete_blockchain": "Supprimer Blockchain",
"delete_blockchain_confirm": "Supprimer & Resynchroniser", "delete_blockchain_confirm": "Supprimer & Resynchroniser",
@@ -1269,6 +1282,7 @@
"transfer_to": "Transférer à :", "transfer_to": "Transférer à :",
"transparent": "Transparent", "transparent": "Transparent",
"transparent_address": "Adresse transparente", "transparent_address": "Adresse transparente",
"try_again": "Réessayer",
"tt_addr_url": "URL de base pour consulter les adresses dans un explorateur de blocs", "tt_addr_url": "URL de base pour consulter les adresses dans un explorateur de blocs",
"tt_address_book": "Gérer les adresses enregistrées pour un envoi rapide", "tt_address_book": "Gérer les adresses enregistrées pour un envoi rapide",
"tt_auto_lock": "Verrouiller le portefeuille après cette durée d'inactivité", "tt_auto_lock": "Verrouiller le portefeuille après cette durée d'inactivité",

View File

@@ -333,6 +333,19 @@
"date": "日付", "date": "日付",
"date_label": "日付:", "date_label": "日付:",
"debug_logging": "デバッグログ", "debug_logging": "デバッグログ",
"decrypt_desc": "ウォレットがエクスポートされ、デーモンが新しい暗号化されていないウォレットで再起動し、すべての鍵が再インポートされます。ウォレットのサイズによっては数分かかる場合があります。",
"decrypt_error_title": "復号に失敗しました",
"decrypt_step_backup": "暗号化されたウォレットをバックアップしています",
"decrypt_step_export": "ウォレットの鍵をエクスポートしています",
"decrypt_step_restart": "デーモンを再起動しています",
"decrypt_step_stop": "デーモンを停止しています",
"decrypt_step_unlock": "ウォレットのロックを解除しています",
"decrypt_success_desc": "ウォレットは暗号化されていない状態になりました。暗号化されたウォレットのバックアップが、データディレクトリに wallet.dat.encrypted.bak として保存されました。",
"decrypt_success_title": "ウォレットの復号に成功しました!",
"decrypt_title": "ウォレットの暗号化を解除",
"decrypt_wait_general": "お待ちください。デーモンが鍵をエクスポートし、再起動して再インポートしています。数分かかる場合があります。",
"decrypt_wait_restart": "デーモンの起動が完了するのを待っています...",
"decrypt_warning": "ウォレットの暗号化が解除されます。秘密鍵は保護されずにディスクに保存されます。",
"delete": "削除", "delete": "削除",
"delete_blockchain": "ブロックチェーンを削除", "delete_blockchain": "ブロックチェーンを削除",
"delete_blockchain_confirm": "削除して再同期", "delete_blockchain_confirm": "削除して再同期",
@@ -1269,6 +1282,7 @@
"transfer_to": "送金先:", "transfer_to": "送金先:",
"transparent": "透明", "transparent": "透明",
"transparent_address": "トランスペアレントアドレス", "transparent_address": "トランスペアレントアドレス",
"try_again": "再試行",
"tt_addr_url": "ブロックエクスプローラーでアドレスを表示するためのベース URL", "tt_addr_url": "ブロックエクスプローラーでアドレスを表示するためのベース URL",
"tt_address_book": "クイック送信用の保存済みアドレスを管理", "tt_address_book": "クイック送信用の保存済みアドレスを管理",
"tt_auto_lock": "この無操作時間後にウォレットをロック", "tt_auto_lock": "この無操作時間後にウォレットをロック",

View File

@@ -333,6 +333,19 @@
"date": "날짜", "date": "날짜",
"date_label": "날짜:", "date_label": "날짜:",
"debug_logging": "디버그 로깅", "debug_logging": "디버그 로깅",
"decrypt_desc": "지갑을 내보내고 데몬을 새 암호화되지 않은 지갑으로 다시 시작한 후 모든 키를 다시 가져옵니다. 지갑 크기에 따라 몇 분 정도 걸릴 수 있습니다.",
"decrypt_error_title": "복호화 실패",
"decrypt_step_backup": "암호화된 지갑 백업 중",
"decrypt_step_export": "지갑 키 내보내는 중",
"decrypt_step_restart": "데몬 다시 시작 중",
"decrypt_step_stop": "데몬 중지 중",
"decrypt_step_unlock": "지갑 잠금 해제 중",
"decrypt_success_desc": "이제 지갑이 암호화되지 않았습니다. 암호화된 지갑의 백업이 데이터 디렉터리에 wallet.dat.encrypted.bak로 저장되었습니다.",
"decrypt_success_title": "지갑 복호화에 성공했습니다!",
"decrypt_title": "지갑 암호화 제거",
"decrypt_wait_general": "잠시 기다려 주세요. 데몬이 키를 내보내고 다시 시작한 후 다시 가져오고 있습니다. 몇 분 정도 걸릴 수 있습니다.",
"decrypt_wait_restart": "데몬이 완전히 시작될 때까지 기다리는 중...",
"decrypt_warning": "지갑의 암호화가 제거됩니다. 개인 키가 보호되지 않은 상태로 디스크에 저장됩니다.",
"delete": "삭제", "delete": "삭제",
"delete_blockchain": "블록체인 삭제", "delete_blockchain": "블록체인 삭제",
"delete_blockchain_confirm": "삭제 후 재동기화", "delete_blockchain_confirm": "삭제 후 재동기화",
@@ -1269,6 +1282,7 @@
"transfer_to": "이체 대상:", "transfer_to": "이체 대상:",
"transparent": "투명", "transparent": "투명",
"transparent_address": "투명 주소", "transparent_address": "투명 주소",
"try_again": "다시 시도",
"tt_addr_url": "블록 탐색기에서 주소를 보기 위한 기본 URL", "tt_addr_url": "블록 탐색기에서 주소를 보기 위한 기본 URL",
"tt_address_book": "빠른 전송을 위해 저장된 주소 관리", "tt_address_book": "빠른 전송을 위해 저장된 주소 관리",
"tt_auto_lock": "이 비활성 시간 후 지갑 잠금", "tt_auto_lock": "이 비활성 시간 후 지갑 잠금",

View File

@@ -333,6 +333,19 @@
"date": "Data", "date": "Data",
"date_label": "Data:", "date_label": "Data:",
"debug_logging": "REGISTRO DE DEPURAÇÃO", "debug_logging": "REGISTRO DE DEPURAÇÃO",
"decrypt_desc": "A carteira será exportada, o daemon reiniciado com uma nova carteira sem criptografia e todas as chaves reimportadas. Isso pode levar alguns minutos, dependendo do tamanho da carteira.",
"decrypt_error_title": "Falha na descriptografia",
"decrypt_step_backup": "Fazendo backup da carteira criptografada",
"decrypt_step_export": "Exportando as chaves da carteira",
"decrypt_step_restart": "Reiniciando o daemon",
"decrypt_step_stop": "Parando o daemon",
"decrypt_step_unlock": "Desbloqueando a carteira",
"decrypt_success_desc": "Sua carteira agora está sem criptografia. Um backup da carteira criptografada foi salvo como wallet.dat.encrypted.bak no seu diretório de dados.",
"decrypt_success_title": "Carteira descriptografada com sucesso!",
"decrypt_title": "Remover criptografia da carteira",
"decrypt_wait_general": "Aguarde. O daemon está exportando chaves, reiniciando e reimportando. Isso pode levar alguns minutos.",
"decrypt_wait_restart": "Aguardando o daemon terminar de iniciar...",
"decrypt_warning": "Isso removerá a criptografia da sua carteira. Suas chaves privadas serão armazenadas sem proteção no disco.",
"delete": "Excluir", "delete": "Excluir",
"delete_blockchain": "Excluir Blockchain", "delete_blockchain": "Excluir Blockchain",
"delete_blockchain_confirm": "Excluir e Ressincronizar", "delete_blockchain_confirm": "Excluir e Ressincronizar",
@@ -1269,6 +1282,7 @@
"transfer_to": "Transferir para:", "transfer_to": "Transferir para:",
"transparent": "Transparente", "transparent": "Transparente",
"transparent_address": "Endereço Transparente", "transparent_address": "Endereço Transparente",
"try_again": "Tentar novamente",
"tt_addr_url": "URL base para visualizar endereços em um explorador de blocos", "tt_addr_url": "URL base para visualizar endereços em um explorador de blocos",
"tt_address_book": "Gerenciar endereços salvos para envio rápido", "tt_address_book": "Gerenciar endereços salvos para envio rápido",
"tt_auto_lock": "Bloquear carteira após este tempo de inatividade", "tt_auto_lock": "Bloquear carteira após este tempo de inatividade",

View File

@@ -333,6 +333,19 @@
"date": "Дата", "date": "Дата",
"date_label": "Дата:", "date_label": "Дата:",
"debug_logging": "ЖУРНАЛ ОТЛАДКИ", "debug_logging": "ЖУРНАЛ ОТЛАДКИ",
"decrypt_desc": "Кошелёк будет экспортирован, демон перезапущен с новым незашифрованным кошельком, и все ключи будут повторно импортированы. Это может занять несколько минут в зависимости от размера кошелька.",
"decrypt_error_title": "Не удалось расшифровать",
"decrypt_step_backup": "Резервное копирование зашифрованного кошелька",
"decrypt_step_export": "Экспорт ключей кошелька",
"decrypt_step_restart": "Перезапуск демона",
"decrypt_step_stop": "Остановка демона",
"decrypt_step_unlock": "Разблокировка кошелька",
"decrypt_success_desc": "Ваш кошелёк теперь не зашифрован. Резервная копия зашифрованного кошелька сохранена как wallet.dat.encrypted.bak в вашем каталоге данных.",
"decrypt_success_title": "Кошелёк успешно расшифрован!",
"decrypt_title": "Удалить шифрование кошелька",
"decrypt_wait_general": "Пожалуйста, подождите. Демон экспортирует ключи, перезапускается и повторно импортирует. Это может занять несколько минут.",
"decrypt_wait_restart": "Ожидание полного запуска демона...",
"decrypt_warning": "Это удалит шифрование вашего кошелька. Ваши приватные ключи будут храниться на диске без защиты.",
"delete": "Удалить", "delete": "Удалить",
"delete_blockchain": "Удалить блокчейн", "delete_blockchain": "Удалить блокчейн",
"delete_blockchain_confirm": "Удалить и пересинхронизировать", "delete_blockchain_confirm": "Удалить и пересинхронизировать",
@@ -1269,6 +1282,7 @@
"transfer_to": "Перевести на:", "transfer_to": "Перевести на:",
"transparent": "Прозрачный", "transparent": "Прозрачный",
"transparent_address": "Прозрачный адрес", "transparent_address": "Прозрачный адрес",
"try_again": "Повторить",
"tt_addr_url": "Базовый URL для просмотра адресов в обозревателе блоков", "tt_addr_url": "Базовый URL для просмотра адресов в обозревателе блоков",
"tt_address_book": "Управление сохранёнными адресами для быстрой отправки", "tt_address_book": "Управление сохранёнными адресами для быстрой отправки",
"tt_auto_lock": "Заблокировать кошелёк после этого времени бездействия", "tt_auto_lock": "Заблокировать кошелёк после этого времени бездействия",

View File

@@ -333,6 +333,19 @@
"date": "日期", "date": "日期",
"date_label": "日期:", "date_label": "日期:",
"debug_logging": "调试日志", "debug_logging": "调试日志",
"decrypt_desc": "钱包将被导出,守护进程将使用全新的未加密钱包重新启动,并重新导入所有密钥。根据钱包大小,这可能需要几分钟。",
"decrypt_error_title": "解密失败",
"decrypt_step_backup": "正在备份加密钱包",
"decrypt_step_export": "正在导出钱包密钥",
"decrypt_step_restart": "正在重启守护进程",
"decrypt_step_stop": "正在停止守护进程",
"decrypt_step_unlock": "正在解锁钱包",
"decrypt_success_desc": "您的钱包现在未加密。加密钱包的备份已保存为 wallet.dat.encrypted.bak位于您的数据目录中。",
"decrypt_success_title": "钱包解密成功!",
"decrypt_title": "移除钱包加密",
"decrypt_wait_general": "请稍候。守护进程正在导出密钥、重启并重新导入。这可能需要几分钟。",
"decrypt_wait_restart": "正在等待守护进程完成启动...",
"decrypt_warning": "这将移除您钱包的加密。您的私钥将以未受保护的方式存储在磁盘上。",
"delete": "删除", "delete": "删除",
"delete_blockchain": "删除区块链", "delete_blockchain": "删除区块链",
"delete_blockchain_confirm": "删除并重新同步", "delete_blockchain_confirm": "删除并重新同步",
@@ -1269,6 +1282,7 @@
"transfer_to": "转账至:", "transfer_to": "转账至:",
"transparent": "透明", "transparent": "透明",
"transparent_address": "透明地址", "transparent_address": "透明地址",
"try_again": "重试",
"tt_addr_url": "在区块浏览器中查看地址的基础 URL", "tt_addr_url": "在区块浏览器中查看地址的基础 URL",
"tt_address_book": "管理已保存的地址以快速发送", "tt_address_book": "管理已保存的地址以快速发送",
"tt_auto_lock": "在此不活动时间后锁定钱包", "tt_auto_lock": "在此不活动时间后锁定钱包",

View File

@@ -1434,26 +1434,21 @@ void App::renderDecryptWalletDialog() {
bool canClose = wallet_security_workflow_.canClose(); bool canClose = wallet_security_workflow_.canClose();
bool* pOpen = canClose ? &show_decrypt_dialog_ : nullptr; bool* pOpen = canClose ? &show_decrypt_dialog_ : nullptr;
if (BeginOverlayDialog("Remove Wallet Encryption", pOpen, 480.0f, 0.94f)) { OverlayDialogSpec ov;
ov.title = TR("decrypt_title"); ov.p_open = pOpen;
ov.style = OverlayStyle::BlurFloat;
ov.cardWidth = 480.0f; ov.idSuffix = "decrypt";
if (BeginOverlayDialog(ov)) {
// ---- Phase 0: Passphrase entry ---- // ---- Phase 0: Passphrase entry ----
if (decryptState.phase == DecryptPhase::PassphraseEntry) { if (decryptState.phase == DecryptPhase::PassphraseEntry) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1, 0.7f, 0.3f, 1)); DialogWarningHeader(TR("decrypt_warning"));
ImGui::TextWrapped(ICON_MD_WARNING
" This will remove encryption from your wallet. "
"Your private keys will be stored unprotected on disk.");
ImGui::PopStyleColor();
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextWrapped( ImGui::TextWrapped("%s", TR("decrypt_desc"));
"The wallet will be exported, the daemon restarted with a fresh "
"unencrypted wallet, and all keys re-imported. This may take "
"several minutes depending on wallet size.");
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing(); ImGui::Spacing();
ImGui::Text("Current Passphrase:"); ImGui::TextUnformatted(TR("change_pass_current"));
ImGui::PushItemWidth(-1); ImGui::PushItemWidth(-1);
bool enterPressed = ImGui::InputText("##decrypt_pass", decrypt_pass_buf_, bool enterPressed = ImGui::InputText("##decrypt_pass", decrypt_pass_buf_,
sizeof(decrypt_pass_buf_), ImGuiInputTextFlags_Password | sizeof(decrypt_pass_buf_), ImGuiInputTextFlags_Password |
@@ -1469,7 +1464,7 @@ void App::renderDecryptWalletDialog() {
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
ImGui::BeginDisabled(!valid || decryptState.inProgress); ImGui::BeginDisabled(!valid || decryptState.inProgress);
if (ui::material::TactileButton("Remove Encryption", ImVec2(btnW, 40)) || (enterPressed && valid)) { if (ui::material::TactileButton(TR("settings_remove_encryption"), ImVec2(btnW, 40)) || (enterPressed && valid)) {
std::string passphrase(decrypt_pass_buf_); std::string passphrase(decrypt_pass_buf_);
memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_)); memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
wallet_security_workflow_.start(std::chrono::steady_clock::now()); wallet_security_workflow_.start(std::chrono::steady_clock::now());
@@ -1660,7 +1655,7 @@ void App::renderDecryptWalletDialog() {
ImGui::EndDisabled(); ImGui::EndDisabled();
ImGui::SameLine(); ImGui::SameLine();
if (ui::material::TactileButton("Cancel", ImVec2(btnW, 40))) { if (ui::material::TactileButton(TR("cancel"), ImVec2(btnW, 40))) {
memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_)); memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
show_decrypt_dialog_ = false; show_decrypt_dialog_ = false;
} }
@@ -1669,11 +1664,11 @@ void App::renderDecryptWalletDialog() {
} else if (decryptState.phase == DecryptPhase::Working) { } else if (decryptState.phase == DecryptPhase::Working) {
// Step checklist // Step checklist
const char* stepLabels[] = { const char* stepLabels[] = {
"Unlocking wallet", TR("decrypt_step_unlock"),
"Exporting wallet keys", TR("decrypt_step_export"),
"Stopping daemon", TR("decrypt_step_stop"),
"Backing up encrypted wallet", TR("decrypt_step_backup"),
"Restarting daemon" TR("decrypt_step_restart")
}; };
const int numSteps = 5; const int numSteps = 5;
@@ -1750,10 +1745,9 @@ void App::renderDecryptWalletDialog() {
// Step-specific hints // Step-specific hints
if (decryptState.step == DecryptStep::RestartDaemon) { if (decryptState.step == DecryptStep::RestartDaemon) {
ImGui::TextWrapped("Waiting for the daemon to finish starting up..."); ImGui::TextWrapped("%s", TR("decrypt_wait_restart"));
} else { } else {
ImGui::TextWrapped("Please wait. The daemon is exporting keys, restarting, " ImGui::TextWrapped("%s", TR("decrypt_wait_general"));
"and re-importing. This may take several minutes.");
} }
// Total elapsed // Total elapsed
@@ -1770,15 +1764,13 @@ void App::renderDecryptWalletDialog() {
ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.5f, 1.0f), ICON_MD_CHECK_CIRCLE); ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.5f, 1.0f), ICON_MD_CHECK_CIRCLE);
ImGui::PopFont(); ImGui::PopFont();
ImGui::SameLine(); ImGui::SameLine();
ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.5f, 1.0f), "Wallet decrypted successfully!"); ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.5f, 1.0f), "%s", TR("decrypt_success_title"));
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextWrapped( ImGui::TextWrapped("%s", TR("decrypt_success_desc"));
"Your wallet is now unencrypted. A backup of the encrypted wallet "
"was saved as wallet.dat.encrypted.bak in your data directory.");
ImGui::Spacing(); ImGui::Spacing();
if (ui::material::TactileButton("Close", ImVec2(-1, 40))) { if (ui::material::TactileButton(TR("close"), ImVec2(-1, 40))) {
show_decrypt_dialog_ = false; show_decrypt_dialog_ = false;
} }
@@ -1788,23 +1780,31 @@ void App::renderDecryptWalletDialog() {
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), ICON_MD_ERROR); ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), ICON_MD_ERROR);
ImGui::PopFont(); ImGui::PopFont();
ImGui::SameLine(); ImGui::SameLine();
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "Decryption failed"); ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%s", TR("decrypt_error_title"));
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextWrapped("%s", decryptState.status.c_str()); ImGui::TextWrapped("%s", decryptState.status.c_str());
ImGui::Spacing(); ImGui::Spacing();
float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f; float btnW = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
if (ui::material::TactileButton("Try Again", ImVec2(btnW, 40))) { if (ui::material::TactileButton(TR("try_again"), ImVec2(btnW, 40))) {
wallet_security_workflow_.reset(); wallet_security_workflow_.reset();
} }
ImGui::SameLine(); ImGui::SameLine();
if (ui::material::TactileButton("Close", ImVec2(btnW, 40))) { if (ui::material::TactileButton(TR("close"), ImVec2(btnW, 40))) {
show_decrypt_dialog_ = false; show_decrypt_dialog_ = false;
} }
} }
EndOverlayDialog(); EndOverlayDialog();
} }
// Wipe the passphrase buffer if the dialog was dismissed (X / Esc / outside-
// click) without submitting. The submit and Cancel paths already memset it;
// this covers the BlurFloat dismiss paths. (canClose is false during Working,
// so an in-flight decrypt cannot be dismissed here.)
if (!show_decrypt_dialog_) {
memset(decrypt_pass_buf_, 0, sizeof(decrypt_pass_buf_));
}
} }
// =========================================================================== // ===========================================================================

View File

@@ -522,6 +522,21 @@ void I18n::loadBuiltinEnglish()
strings_["change_pass_current"] = "Current Passphrase:"; strings_["change_pass_current"] = "Current Passphrase:";
strings_["change_pass_new"] = "New Passphrase:"; strings_["change_pass_new"] = "New Passphrase:";
strings_["change_pass_confirm"] = "Confirm New:"; strings_["change_pass_confirm"] = "Confirm New:";
// --- Remove-encryption (decrypt) dialog (settings) ---
strings_["decrypt_title"] = "Remove Wallet Encryption";
strings_["decrypt_warning"] = "This will remove encryption from your wallet. Your private keys will be stored unprotected on disk.";
strings_["decrypt_desc"] = "The wallet will be exported, the daemon restarted with a fresh unencrypted wallet, and all keys re-imported. This may take several minutes depending on wallet size.";
strings_["decrypt_step_unlock"] = "Unlocking wallet";
strings_["decrypt_step_export"] = "Exporting wallet keys";
strings_["decrypt_step_stop"] = "Stopping daemon";
strings_["decrypt_step_backup"] = "Backing up encrypted wallet";
strings_["decrypt_step_restart"] = "Restarting daemon";
strings_["decrypt_wait_restart"] = "Waiting for the daemon to finish starting up...";
strings_["decrypt_wait_general"] = "Please wait. The daemon is exporting keys, restarting, and re-importing. This may take several minutes.";
strings_["decrypt_success_title"] = "Wallet decrypted successfully!";
strings_["decrypt_success_desc"] = "Your wallet is now unencrypted. A backup of the encrypted wallet was saved as wallet.dat.encrypted.bak in your data directory.";
strings_["decrypt_error_title"] = "Decryption failed";
strings_["try_again"] = "Try Again";
strings_["wiz_confirm"] = "Confirm:"; strings_["wiz_confirm"] = "Confirm:";
strings_["wiz_strength_weak"] = "Weak"; strings_["wiz_strength_weak"] = "Weak";
strings_["wiz_strength_fair"] = "Fair"; strings_["wiz_strength_fair"] = "Fair";