feat(wallet): give new full-node wallets a BIP39 seed phrase + backup UI
New full-node wallets now get a real 24-word recovery phrase, and users can reveal/back it up from Settings. - Pass -usemnemonic=1 to dragonxd. The daemon reads it only inside GenerateNewSeed() when a wallet has no seed yet, so it is inert on existing wallets (safe to pass unconditionally) and makes every freshly-created wallet mnemonic-backed — its phrase is then exportable via z_exportmnemonic and portable to SDXLite/ObsidianDragonLite. (Existing/legacy wallets are unaffected and keep using the chat identity's z_exportkey fallback.) - App::exportSeedPhrase() wraps z_exportmnemonic on the RPC worker with sodium_memzero wiping on every path; it flags the daemon's "not derived from a mnemonic" error as a legacy wallet rather than a failure. - New "Back up seed phrase" modal (Settings -> Backup & Data, full-node gated): reveals the phrase in a numbered word grid, copy (45s clipboard auto-clear) + save-to-file (0600), wipes the secret on every close path incl. dismiss-mid-fetch. Shared ui/windows/seed_display.h grid helper. - One-time nudge (settings flag seed_backup_reminded) toasts mnemonic-wallet users to back up their phrase; legacy wallets are not nagged. - 15 new strings translated into all 8 languages (additive, 957->972 keys); CJK subset font rebuilt for the new glyphs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
@@ -641,6 +641,20 @@
|
|||||||
"sb_warming_up": "Aufwärmen...",
|
"sb_warming_up": "Aufwärmen...",
|
||||||
"search_placeholder": "Suchen...",
|
"search_placeholder": "Suchen...",
|
||||||
"security": "SICHERHEIT",
|
"security": "SICHERHEIT",
|
||||||
|
"seed_backup_button": "Wiederherstellungsphrase",
|
||||||
|
"seed_backup_close": "Schließen",
|
||||||
|
"seed_backup_copy": "Kopieren",
|
||||||
|
"seed_backup_intro": "Diese Wörter sind die Master-Sicherung Ihrer Wallet. Wer sie besitzt, kontrolliert Ihre Gelder.",
|
||||||
|
"seed_backup_load_failed": "Die Wiederherstellungsphrase konnte nicht geladen werden.",
|
||||||
|
"seed_backup_loading": "Ihre Wiederherstellungsphrase wird geladen…",
|
||||||
|
"seed_backup_locked": "Entsperren Sie Ihre Wallet, um ihre Wiederherstellungsphrase anzuzeigen.",
|
||||||
|
"seed_backup_none": "Diese Wallet ist älter als Wiederherstellungsphrasen und hat daher keine 24-Wort-Phrase. Sichern Sie sie stattdessen mit „Sicherung...“ oder durch den Export Ihrer privaten Schlüssel.",
|
||||||
|
"seed_backup_reminder": "Ihre Wallet hat eine 24-Wort-Wiederherstellungsphrase. Sichern Sie sie jetzt unter Einstellungen → Node & Sicherheit.",
|
||||||
|
"seed_backup_save": "In Datei speichern…",
|
||||||
|
"seed_backup_save_failed": "Konnte nicht schreiben ",
|
||||||
|
"seed_backup_saved": "Gespeichert unter ",
|
||||||
|
"seed_backup_title": "Wiederherstellungsphrase sichern",
|
||||||
|
"seed_backup_warning": "Schreiben Sie sie der Reihe nach auf, bewahren Sie sie offline auf und teilen oder fotografieren Sie sie niemals. Wenn Sie sie verlieren, können Ihre Gelder nicht wiederhergestellt werden.",
|
||||||
"select_address": "Adresse auswählen...",
|
"select_address": "Adresse auswählen...",
|
||||||
"select_receiving_address": "Empfangsadresse auswählen...",
|
"select_receiving_address": "Empfangsadresse auswählen...",
|
||||||
"select_source_address": "Quelladresse auswählen...",
|
"select_source_address": "Quelladresse auswählen...",
|
||||||
@@ -889,6 +903,7 @@
|
|||||||
"tt_save_ztx": "Z-Adresse-Transaktionsverlauf lokal für schnelleres Laden speichern",
|
"tt_save_ztx": "Z-Adresse-Transaktionsverlauf lokal für schnelleres Laden speichern",
|
||||||
"tt_scan_themes": "Nach neuen Themes suchen.\\nTheme-Ordner ablegen in:\\n%s",
|
"tt_scan_themes": "Nach neuen Themes suchen.\\nTheme-Ordner ablegen in:\\n%s",
|
||||||
"tt_scanline": "CRT-Scanlinieneffekt in der Konsole",
|
"tt_scanline": "CRT-Scanlinieneffekt in der Konsole",
|
||||||
|
"tt_seed_backup": "Die 24-Wort-Wiederherstellungsphrase Ihrer Wallet anzeigen und sichern",
|
||||||
"tt_set_pin": "Eine 4-8-stellige PIN für schnelles Entsperren festlegen",
|
"tt_set_pin": "Eine 4-8-stellige PIN für schnelles Entsperren festlegen",
|
||||||
"tt_shield_mining": "Transparente Mining-Belohnungen an eine geschirmte Adresse verschieben",
|
"tt_shield_mining": "Transparente Mining-Belohnungen an eine geschirmte Adresse verschieben",
|
||||||
"tt_simple_bg": "Einen einfachen Verlauf für den Hintergrund verwenden\\nHotkey: Ctrl+Up",
|
"tt_simple_bg": "Einen einfachen Verlauf für den Hintergrund verwenden\\nHotkey: Ctrl+Up",
|
||||||
|
|||||||
@@ -641,6 +641,20 @@
|
|||||||
"sb_warming_up": "Calentando...",
|
"sb_warming_up": "Calentando...",
|
||||||
"search_placeholder": "Buscar...",
|
"search_placeholder": "Buscar...",
|
||||||
"security": "SEGURIDAD",
|
"security": "SEGURIDAD",
|
||||||
|
"seed_backup_button": "Frase de recuperación",
|
||||||
|
"seed_backup_close": "Cerrar",
|
||||||
|
"seed_backup_copy": "Copiar",
|
||||||
|
"seed_backup_intro": "Estas palabras son el respaldo maestro de tu cartera. Cualquiera que las tenga controla tus fondos.",
|
||||||
|
"seed_backup_load_failed": "No se pudo cargar la frase de recuperación.",
|
||||||
|
"seed_backup_loading": "Cargando tu frase de recuperación…",
|
||||||
|
"seed_backup_locked": "Desbloquea tu cartera para revelar su frase de recuperación.",
|
||||||
|
"seed_backup_none": "Esta cartera es anterior a las frases de recuperación, por lo que no tiene una frase de 24 palabras. Respáldala con «Respaldo...» o exportando tus claves privadas en su lugar.",
|
||||||
|
"seed_backup_reminder": "Tu cartera tiene una frase de recuperación de 24 palabras. Respáldala ahora en Configuración → Nodo y Seguridad.",
|
||||||
|
"seed_backup_save": "Guardar en archivo…",
|
||||||
|
"seed_backup_save_failed": "No se pudo escribir ",
|
||||||
|
"seed_backup_saved": "Guardado en ",
|
||||||
|
"seed_backup_title": "Respaldar frase de recuperación",
|
||||||
|
"seed_backup_warning": "Anótalas en orden, guárdalas sin conexión y nunca las compartas ni las fotografíes. Si las pierdes, tus fondos no se podrán recuperar.",
|
||||||
"select_address": "Seleccionar dirección...",
|
"select_address": "Seleccionar dirección...",
|
||||||
"select_receiving_address": "Seleccionar dirección de recepción...",
|
"select_receiving_address": "Seleccionar dirección de recepción...",
|
||||||
"select_source_address": "Seleccionar dirección de origen...",
|
"select_source_address": "Seleccionar dirección de origen...",
|
||||||
@@ -889,6 +903,7 @@
|
|||||||
"tt_save_ztx": "Almacenar historial de transacciones de z-address localmente para carga más rápida",
|
"tt_save_ztx": "Almacenar historial de transacciones de z-address localmente para carga más rápida",
|
||||||
"tt_scan_themes": "Buscar nuevos temas.\\nColoque carpetas de temas en:\\n%s",
|
"tt_scan_themes": "Buscar nuevos temas.\\nColoque carpetas de temas en:\\n%s",
|
||||||
"tt_scanline": "Efecto de líneas de escaneo CRT en la consola",
|
"tt_scanline": "Efecto de líneas de escaneo CRT en la consola",
|
||||||
|
"tt_seed_backup": "Muestra y respalda la frase de recuperación de 24 palabras de tu cartera",
|
||||||
"tt_set_pin": "Establecer un PIN de 4-8 dígitos para desbloqueo rápido",
|
"tt_set_pin": "Establecer un PIN de 4-8 dígitos para desbloqueo rápido",
|
||||||
"tt_shield_mining": "Mover recompensas de minería transparentes a una dirección blindada",
|
"tt_shield_mining": "Mover recompensas de minería transparentes a una dirección blindada",
|
||||||
"tt_simple_bg": "Usar un gradiente simple para el fondo\\nAtajo: Ctrl+Up",
|
"tt_simple_bg": "Usar un gradiente simple para el fondo\\nAtajo: Ctrl+Up",
|
||||||
|
|||||||
@@ -641,6 +641,20 @@
|
|||||||
"sb_warming_up": "Démarrage...",
|
"sb_warming_up": "Démarrage...",
|
||||||
"search_placeholder": "Rechercher...",
|
"search_placeholder": "Rechercher...",
|
||||||
"security": "SÉCURITÉ",
|
"security": "SÉCURITÉ",
|
||||||
|
"seed_backup_button": "Phrase de récupération",
|
||||||
|
"seed_backup_close": "Fermer",
|
||||||
|
"seed_backup_copy": "Copier",
|
||||||
|
"seed_backup_intro": "Ces mots sont la sauvegarde principale de votre portefeuille. Quiconque les possède contrôle vos fonds.",
|
||||||
|
"seed_backup_load_failed": "Impossible de charger la phrase de récupération.",
|
||||||
|
"seed_backup_loading": "Chargement de votre phrase de récupération…",
|
||||||
|
"seed_backup_locked": "Déverrouillez votre portefeuille pour révéler sa phrase de récupération.",
|
||||||
|
"seed_backup_none": "Ce portefeuille est antérieur aux phrases de récupération, il n'a donc pas de phrase de 24 mots. Sauvegardez-le plutôt avec « Sauvegarde » ou en exportant vos clés privées.",
|
||||||
|
"seed_backup_reminder": "Votre portefeuille possède une phrase de récupération de 24 mots. Sauvegardez-la maintenant dans Paramètres → Nœud et sécurité.",
|
||||||
|
"seed_backup_save": "Enregistrer dans un fichier…",
|
||||||
|
"seed_backup_save_failed": "Impossible d'écrire ",
|
||||||
|
"seed_backup_saved": "Enregistré dans ",
|
||||||
|
"seed_backup_title": "Sauvegarder la phrase de récupération",
|
||||||
|
"seed_backup_warning": "Notez-les dans l'ordre, conservez-les hors ligne, et ne les partagez ni ne les photographiez jamais. Si vous les perdez, vos fonds ne pourront pas être récupérés.",
|
||||||
"select_address": "Sélectionner une adresse...",
|
"select_address": "Sélectionner une adresse...",
|
||||||
"select_receiving_address": "Sélectionner une adresse de réception...",
|
"select_receiving_address": "Sélectionner une adresse de réception...",
|
||||||
"select_source_address": "Sélectionner une adresse source...",
|
"select_source_address": "Sélectionner une adresse source...",
|
||||||
@@ -889,6 +903,7 @@
|
|||||||
"tt_save_ztx": "Stocker l'historique des transactions z-address localement pour un chargement plus rapide",
|
"tt_save_ztx": "Stocker l'historique des transactions z-address localement pour un chargement plus rapide",
|
||||||
"tt_scan_themes": "Rechercher de nouveaux thèmes.\\nPlacez les dossiers de thèmes dans :\\n%s",
|
"tt_scan_themes": "Rechercher de nouveaux thèmes.\\nPlacez les dossiers de thèmes dans :\\n%s",
|
||||||
"tt_scanline": "Effet de lignes de balayage CRT dans la console",
|
"tt_scanline": "Effet de lignes de balayage CRT dans la console",
|
||||||
|
"tt_seed_backup": "Afficher et sauvegarder la phrase de récupération de 24 mots de votre portefeuille",
|
||||||
"tt_set_pin": "Définir un PIN de 4-8 chiffres pour un déverrouillage rapide",
|
"tt_set_pin": "Définir un PIN de 4-8 chiffres pour un déverrouillage rapide",
|
||||||
"tt_shield_mining": "Déplacer les récompenses de minage transparentes vers une adresse blindée",
|
"tt_shield_mining": "Déplacer les récompenses de minage transparentes vers une adresse blindée",
|
||||||
"tt_simple_bg": "Utiliser un dégradé simple pour l'arrière-plan\\nRaccourci : Ctrl+Up",
|
"tt_simple_bg": "Utiliser un dégradé simple pour l'arrière-plan\\nRaccourci : Ctrl+Up",
|
||||||
|
|||||||
@@ -641,6 +641,20 @@
|
|||||||
"sb_warming_up": "ウォームアップ中...",
|
"sb_warming_up": "ウォームアップ中...",
|
||||||
"search_placeholder": "検索...",
|
"search_placeholder": "検索...",
|
||||||
"security": "セキュリティ",
|
"security": "セキュリティ",
|
||||||
|
"seed_backup_button": "シードフレーズ",
|
||||||
|
"seed_backup_close": "閉じる",
|
||||||
|
"seed_backup_copy": "コピー",
|
||||||
|
"seed_backup_intro": "これらの単語はウォレットのマスターバックアップです。これらを持つ人は誰でもあなたの資金を管理できます。",
|
||||||
|
"seed_backup_load_failed": "シードフレーズを読み込めませんでした。",
|
||||||
|
"seed_backup_loading": "シードフレーズを読み込み中...",
|
||||||
|
"seed_backup_locked": "シードフレーズを表示するにはウォレットのロックを解除してください。",
|
||||||
|
"seed_backup_none": "このウォレットはシードフレーズより前のものなので、24単語のフレーズはありません。代わりに「バックアップ...」で、または秘密鍵をエクスポートしてバックアップしてください。",
|
||||||
|
"seed_backup_reminder": "ウォレットには24単語の復元シードフレーズがあります。今すぐ 設定 → ノードとセキュリティ でバックアップしてください。",
|
||||||
|
"seed_backup_save": "ファイルに保存...",
|
||||||
|
"seed_backup_save_failed": "書き込めませんでした: ",
|
||||||
|
"seed_backup_saved": "保存先: ",
|
||||||
|
"seed_backup_title": "シードフレーズをバックアップ",
|
||||||
|
"seed_backup_warning": "順番どおりに書き写し、オフラインで保管してください。決して共有や撮影をしないでください。紛失すると資金を復元できません。",
|
||||||
"select_address": "アドレスを選択...",
|
"select_address": "アドレスを選択...",
|
||||||
"select_receiving_address": "受信アドレスを選択...",
|
"select_receiving_address": "受信アドレスを選択...",
|
||||||
"select_source_address": "送信元アドレスを選択...",
|
"select_source_address": "送信元アドレスを選択...",
|
||||||
@@ -889,6 +903,7 @@
|
|||||||
"tt_save_ztx": "z-address トランザクション履歴をローカルに保存して高速読み込み",
|
"tt_save_ztx": "z-address トランザクション履歴をローカルに保存して高速読み込み",
|
||||||
"tt_scan_themes": "新しいテーマをスキャン。\\nテーマフォルダーをここに配置:\\n%s",
|
"tt_scan_themes": "新しいテーマをスキャン。\\nテーマフォルダーをここに配置:\\n%s",
|
||||||
"tt_scanline": "コンソールでの CRT スキャンライン効果",
|
"tt_scanline": "コンソールでの CRT スキャンライン効果",
|
||||||
|
"tt_seed_backup": "ウォレットの24単語の復元シードフレーズを表示してバックアップします",
|
||||||
"tt_set_pin": "クイックアンロック用の 4-8 桁 PIN を設定",
|
"tt_set_pin": "クイックアンロック用の 4-8 桁 PIN を設定",
|
||||||
"tt_shield_mining": "透明マイニング報酬をシールドアドレスに移動",
|
"tt_shield_mining": "透明マイニング報酬をシールドアドレスに移動",
|
||||||
"tt_simple_bg": "背景にシンプルなグラデーションを使用\\nホットキー:Ctrl+Up",
|
"tt_simple_bg": "背景にシンプルなグラデーションを使用\\nホットキー:Ctrl+Up",
|
||||||
|
|||||||
@@ -641,6 +641,20 @@
|
|||||||
"sb_warming_up": "워밍업 중...",
|
"sb_warming_up": "워밍업 중...",
|
||||||
"search_placeholder": "검색...",
|
"search_placeholder": "검색...",
|
||||||
"security": "보안",
|
"security": "보안",
|
||||||
|
"seed_backup_button": "시드 문구",
|
||||||
|
"seed_backup_close": "닫기",
|
||||||
|
"seed_backup_copy": "복사",
|
||||||
|
"seed_backup_intro": "이 단어들은 지갑의 마스터 백업입니다. 이 단어들을 가진 사람은 누구나 당신의 자금을 통제할 수 있습니다.",
|
||||||
|
"seed_backup_load_failed": "시드 문구를 불러올 수 없습니다.",
|
||||||
|
"seed_backup_loading": "시드 문구를 불러오는 중…",
|
||||||
|
"seed_backup_locked": "시드 문구를 표시하려면 지갑의 잠금을 해제하세요.",
|
||||||
|
"seed_backup_none": "이 지갑은 시드 문구 도입 이전에 만들어져 24단어 문구가 없습니다. 대신 \"백업...\"을 사용하거나 개인 키를 내보내 백업하세요.",
|
||||||
|
"seed_backup_reminder": "지갑에 24단어 복구 시드 문구가 있습니다. 지금 설정 → 노드 및 보안에서 백업하세요.",
|
||||||
|
"seed_backup_save": "파일로 저장…",
|
||||||
|
"seed_backup_save_failed": "쓸 수 없습니다: ",
|
||||||
|
"seed_backup_saved": "저장됨: ",
|
||||||
|
"seed_backup_title": "시드 문구 백업",
|
||||||
|
"seed_backup_warning": "순서대로 종이에 적어 오프라인으로 보관하고, 절대 공유하거나 사진으로 찍지 마세요. 이 단어들을 잃어버리면 자금을 복구할 수 없습니다.",
|
||||||
"select_address": "주소 선택...",
|
"select_address": "주소 선택...",
|
||||||
"select_receiving_address": "수신 주소 선택...",
|
"select_receiving_address": "수신 주소 선택...",
|
||||||
"select_source_address": "보낼 주소 선택...",
|
"select_source_address": "보낼 주소 선택...",
|
||||||
@@ -889,6 +903,7 @@
|
|||||||
"tt_save_ztx": "z-address 거래 기록을 로컬에 저장하여 빠른 로딩",
|
"tt_save_ztx": "z-address 거래 기록을 로컬에 저장하여 빠른 로딩",
|
||||||
"tt_scan_themes": "새 테마 검색.\\n테마 폴더를 여기에 배치:\\n%s",
|
"tt_scan_themes": "새 테마 검색.\\n테마 폴더를 여기에 배치:\\n%s",
|
||||||
"tt_scanline": "콘솔에서 CRT 스캔라인 효과",
|
"tt_scanline": "콘솔에서 CRT 스캔라인 효과",
|
||||||
|
"tt_seed_backup": "지갑의 24단어 복구 시드 문구를 표시하고 백업합니다",
|
||||||
"tt_set_pin": "빠른 잠금 해제를 위한 4-8자리 PIN 설정",
|
"tt_set_pin": "빠른 잠금 해제를 위한 4-8자리 PIN 설정",
|
||||||
"tt_shield_mining": "투명 채굴 보상을 차폐 주소로 이동",
|
"tt_shield_mining": "투명 채굴 보상을 차폐 주소로 이동",
|
||||||
"tt_simple_bg": "배경에 단순 그라데이션 사용\\n단축키: Ctrl+Up",
|
"tt_simple_bg": "배경에 단순 그라데이션 사용\\n단축키: Ctrl+Up",
|
||||||
|
|||||||
@@ -641,6 +641,20 @@
|
|||||||
"sb_warming_up": "Aquecendo...",
|
"sb_warming_up": "Aquecendo...",
|
||||||
"search_placeholder": "Pesquisar...",
|
"search_placeholder": "Pesquisar...",
|
||||||
"security": "SEGURANÇA",
|
"security": "SEGURANÇA",
|
||||||
|
"seed_backup_button": "Frase de recuperação",
|
||||||
|
"seed_backup_close": "Fechar",
|
||||||
|
"seed_backup_copy": "Copiar",
|
||||||
|
"seed_backup_intro": "Estas palavras são o backup mestre da sua carteira. Qualquer pessoa que as tenha controla seus fundos.",
|
||||||
|
"seed_backup_load_failed": "Não foi possível carregar a frase de recuperação.",
|
||||||
|
"seed_backup_loading": "Carregando sua frase de recuperação…",
|
||||||
|
"seed_backup_locked": "Desbloqueie sua carteira para revelar sua frase de recuperação.",
|
||||||
|
"seed_backup_none": "Esta carteira é anterior às frases de recuperação, portanto não possui uma frase de 24 palavras. Faça o backup dela com \"Backup...\" ou exportando suas chaves privadas.",
|
||||||
|
"seed_backup_reminder": "Sua carteira tem uma frase de recuperação de 24 palavras. Faça o backup dela agora em Configurações → Nó e Segurança.",
|
||||||
|
"seed_backup_save": "Salvar em arquivo…",
|
||||||
|
"seed_backup_save_failed": "Não foi possível gravar ",
|
||||||
|
"seed_backup_saved": "Salvo em ",
|
||||||
|
"seed_backup_title": "Backup da frase de recuperação",
|
||||||
|
"seed_backup_warning": "Anote-as em ordem, guarde-as offline e nunca as compartilhe ou fotografe. Se você as perder, seus fundos não poderão ser recuperados.",
|
||||||
"select_address": "Selecionar endereço...",
|
"select_address": "Selecionar endereço...",
|
||||||
"select_receiving_address": "Selecionar endereço de recebimento...",
|
"select_receiving_address": "Selecionar endereço de recebimento...",
|
||||||
"select_source_address": "Selecionar endereço de origem...",
|
"select_source_address": "Selecionar endereço de origem...",
|
||||||
@@ -889,6 +903,7 @@
|
|||||||
"tt_save_ztx": "Armazenar histórico de transações z-address localmente para carregamento mais rápido",
|
"tt_save_ztx": "Armazenar histórico de transações z-address localmente para carregamento mais rápido",
|
||||||
"tt_scan_themes": "Procurar novos temas.\\nColoque pastas de temas em:\\n%s",
|
"tt_scan_themes": "Procurar novos temas.\\nColoque pastas de temas em:\\n%s",
|
||||||
"tt_scanline": "Efeito de linhas de varredura CRT no console",
|
"tt_scanline": "Efeito de linhas de varredura CRT no console",
|
||||||
|
"tt_seed_backup": "Mostrar e fazer backup da frase de recuperação de 24 palavras da sua carteira",
|
||||||
"tt_set_pin": "Definir um PIN de 4-8 dígitos para desbloqueio rápido",
|
"tt_set_pin": "Definir um PIN de 4-8 dígitos para desbloqueio rápido",
|
||||||
"tt_shield_mining": "Mover recompensas de mineração transparentes para um endereço blindado",
|
"tt_shield_mining": "Mover recompensas de mineração transparentes para um endereço blindado",
|
||||||
"tt_simple_bg": "Usar um gradiente simples para o fundo\\nAtalho: Ctrl+Up",
|
"tt_simple_bg": "Usar um gradiente simples para o fundo\\nAtalho: Ctrl+Up",
|
||||||
|
|||||||
@@ -641,6 +641,20 @@
|
|||||||
"sb_warming_up": "Прогрев...",
|
"sb_warming_up": "Прогрев...",
|
||||||
"search_placeholder": "Поиск...",
|
"search_placeholder": "Поиск...",
|
||||||
"security": "БЕЗОПАСНОСТЬ",
|
"security": "БЕЗОПАСНОСТЬ",
|
||||||
|
"seed_backup_button": "Сид-фраза",
|
||||||
|
"seed_backup_close": "Закрыть",
|
||||||
|
"seed_backup_copy": "Копировать",
|
||||||
|
"seed_backup_intro": "Эти слова — главная резервная копия вашего кошелька. Любой, у кого они есть, управляет вашими средствами.",
|
||||||
|
"seed_backup_load_failed": "Не удалось загрузить сид-фразу.",
|
||||||
|
"seed_backup_loading": "Загрузка вашей сид-фразы…",
|
||||||
|
"seed_backup_locked": "Разблокируйте кошелёк, чтобы показать его сид-фразу.",
|
||||||
|
"seed_backup_none": "Этот кошелёк создан до появления сид-фраз, поэтому у него нет фразы из 24 слов. Создайте резервную копию с помощью «Резервная копия...» или вместо этого экспортируйте свои приватные ключи.",
|
||||||
|
"seed_backup_reminder": "У вашего кошелька есть сид-фраза восстановления из 24 слов. Создайте её резервную копию прямо сейчас в Настройки → Узел и безопасность.",
|
||||||
|
"seed_backup_save": "Сохранить в файл…",
|
||||||
|
"seed_backup_save_failed": "Не удалось записать ",
|
||||||
|
"seed_backup_saved": "Сохранено в ",
|
||||||
|
"seed_backup_title": "Резервная копия сид-фразы",
|
||||||
|
"seed_backup_warning": "Запишите их по порядку, храните офлайн и никогда не делитесь ими и не фотографируйте их. Если вы их потеряете, ваши средства не удастся восстановить.",
|
||||||
"select_address": "Выбрать адрес...",
|
"select_address": "Выбрать адрес...",
|
||||||
"select_receiving_address": "Выбрать адрес получения...",
|
"select_receiving_address": "Выбрать адрес получения...",
|
||||||
"select_source_address": "Выбрать адрес-источник...",
|
"select_source_address": "Выбрать адрес-источник...",
|
||||||
@@ -889,6 +903,7 @@
|
|||||||
"tt_save_ztx": "Хранить историю транзакций z-адреса локально для более быстрой загрузки",
|
"tt_save_ztx": "Хранить историю транзакций z-адреса локально для более быстрой загрузки",
|
||||||
"tt_scan_themes": "Поиск новых тем.\\nРазместите папки тем в:\\n%s",
|
"tt_scan_themes": "Поиск новых тем.\\nРазместите папки тем в:\\n%s",
|
||||||
"tt_scanline": "Эффект развёртки ЭЛТ в консоли",
|
"tt_scanline": "Эффект развёртки ЭЛТ в консоли",
|
||||||
|
"tt_seed_backup": "Показать и создать резервную копию сид-фразы восстановления вашего кошелька из 24 слов",
|
||||||
"tt_set_pin": "Установить 4-8-значный PIN для быстрой разблокировки",
|
"tt_set_pin": "Установить 4-8-значный PIN для быстрой разблокировки",
|
||||||
"tt_shield_mining": "Перевести прозрачные вознаграждения за майнинг на экранированный адрес",
|
"tt_shield_mining": "Перевести прозрачные вознаграждения за майнинг на экранированный адрес",
|
||||||
"tt_simple_bg": "Использовать простой градиент для фона\\nГорячая клавиша: Ctrl+Up",
|
"tt_simple_bg": "Использовать простой градиент для фона\\nГорячая клавиша: Ctrl+Up",
|
||||||
|
|||||||
@@ -641,6 +641,20 @@
|
|||||||
"sb_warming_up": "正在预热...",
|
"sb_warming_up": "正在预热...",
|
||||||
"search_placeholder": "搜索...",
|
"search_placeholder": "搜索...",
|
||||||
"security": "安全",
|
"security": "安全",
|
||||||
|
"seed_backup_button": "助记词",
|
||||||
|
"seed_backup_close": "关闭",
|
||||||
|
"seed_backup_copy": "复制",
|
||||||
|
"seed_backup_intro": "这些词是您钱包的主备份。任何拥有它们的人都能控制您的资金。",
|
||||||
|
"seed_backup_load_failed": "无法加载助记词。",
|
||||||
|
"seed_backup_loading": "正在加载您的助记词…",
|
||||||
|
"seed_backup_locked": "解锁您的钱包以显示其助记词。",
|
||||||
|
"seed_backup_none": "此钱包早于助记词功能,因此没有 24 词助记词。请改用“备份钱包”或导出私钥来备份它。",
|
||||||
|
"seed_backup_reminder": "您的钱包有一个 24 词恢复助记词。请立即在“设置 → 节点与安全”中备份它。",
|
||||||
|
"seed_backup_save": "保存到文件…",
|
||||||
|
"seed_backup_save_failed": "无法写入 ",
|
||||||
|
"seed_backup_saved": "已保存到 ",
|
||||||
|
"seed_backup_title": "备份助记词",
|
||||||
|
"seed_backup_warning": "请按顺序将它们抄写下来,离线保存,切勿分享或拍照。一旦丢失,您的资金将无法恢复。",
|
||||||
"select_address": "选择地址...",
|
"select_address": "选择地址...",
|
||||||
"select_receiving_address": "选择接收地址...",
|
"select_receiving_address": "选择接收地址...",
|
||||||
"select_source_address": "选择来源地址...",
|
"select_source_address": "选择来源地址...",
|
||||||
@@ -889,6 +903,7 @@
|
|||||||
"tt_save_ztx": "将 z-address 交易历史存储在本地以加快加载速度",
|
"tt_save_ztx": "将 z-address 交易历史存储在本地以加快加载速度",
|
||||||
"tt_scan_themes": "扫描新主题。\\n将主题文件夹放在:\\n%s",
|
"tt_scan_themes": "扫描新主题。\\n将主题文件夹放在:\\n%s",
|
||||||
"tt_scanline": "控制台中的 CRT 扫描线效果",
|
"tt_scanline": "控制台中的 CRT 扫描线效果",
|
||||||
|
"tt_seed_backup": "显示并备份您钱包的 24 词恢复助记词",
|
||||||
"tt_set_pin": "设置 4-8 位 PIN 以快速解锁",
|
"tt_set_pin": "设置 4-8 位 PIN 以快速解锁",
|
||||||
"tt_shield_mining": "将透明挖矿奖励转移到屏蔽地址",
|
"tt_shield_mining": "将透明挖矿奖励转移到屏蔽地址",
|
||||||
"tt_simple_bg": "使用简单渐变作为背景\\n快捷键:Ctrl+Up",
|
"tt_simple_bg": "使用简单渐变作为背景\\n快捷键:Ctrl+Up",
|
||||||
|
|||||||
106
src/app.cpp
106
src/app.cpp
@@ -33,6 +33,7 @@
|
|||||||
#include "ui/windows/network_tab.h"
|
#include "ui/windows/network_tab.h"
|
||||||
#include "ui/windows/console_command_executor.h"
|
#include "ui/windows/console_command_executor.h"
|
||||||
#include "ui/windows/explorer_tab.h"
|
#include "ui/windows/explorer_tab.h"
|
||||||
|
#include "ui/windows/seed_display.h"
|
||||||
#include "ui/windows/market_tab.h"
|
#include "ui/windows/market_tab.h"
|
||||||
#include "ui/windows/settings_window.h"
|
#include "ui/windows/settings_window.h"
|
||||||
#include "ui/windows/about_dialog.h"
|
#include "ui/windows/about_dialog.h"
|
||||||
@@ -715,6 +716,9 @@ void App::update()
|
|||||||
// it can act; compiled away when the feature is off (constexpr gate inside).
|
// it can act; compiled away when the feature is off (constexpr gate inside).
|
||||||
maybeProvisionChatIdentity();
|
maybeProvisionChatIdentity();
|
||||||
|
|
||||||
|
// One-time reminder to back up the wallet's seed phrase (mnemonic wallets only).
|
||||||
|
maybeRemindSeedBackup();
|
||||||
|
|
||||||
// Auto-lock check (only when connected + encrypted + unlocked)
|
// Auto-lock check (only when connected + encrypted + unlocked)
|
||||||
if (state_.connected && state_.isUnlocked()) {
|
if (state_.connected && state_.isUnlocked()) {
|
||||||
checkAutoLock();
|
checkAutoLock();
|
||||||
@@ -1893,6 +1897,10 @@ void App::render()
|
|||||||
renderBackupDialog();
|
renderBackupDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (show_seed_backup_) {
|
||||||
|
renderSeedBackupDialog();
|
||||||
|
}
|
||||||
|
|
||||||
// Security overlay dialogs (encrypt, decrypt, PIN) are rendered AFTER ImGui::End()
|
// Security overlay dialogs (encrypt, decrypt, PIN) are rendered AFTER ImGui::End()
|
||||||
// to ensure they appear on top of all other content
|
// to ensure they appear on top of all other content
|
||||||
|
|
||||||
@@ -2921,7 +2929,103 @@ void App::renderBackupDialog()
|
|||||||
backup_status_.clear();
|
backup_status_.clear();
|
||||||
s_backup_confirm_overwrite = false;
|
s_backup_confirm_overwrite = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui::material::EndOverlayDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
void App::renderSeedBackupDialog()
|
||||||
|
{
|
||||||
|
// Wipe the revealed phrase and reset the dialog's state.
|
||||||
|
auto closeAndWipe = [this]() {
|
||||||
|
if (!seed_backup_phrase_.empty())
|
||||||
|
sodium_memzero(&seed_backup_phrase_[0], seed_backup_phrase_.size());
|
||||||
|
seed_backup_phrase_.clear();
|
||||||
|
seed_backup_status_.clear();
|
||||||
|
seed_backup_fetch_started_ = false;
|
||||||
|
seed_backup_loading_ = false;
|
||||||
|
seed_backup_no_mnemonic_ = false;
|
||||||
|
show_seed_backup_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Kick off the async z_exportmnemonic fetch once, and only while unlocked.
|
||||||
|
if (!seed_backup_fetch_started_ && !state_.isLocked()) {
|
||||||
|
seed_backup_fetch_started_ = true;
|
||||||
|
seed_backup_loading_ = true;
|
||||||
|
seed_backup_no_mnemonic_ = false;
|
||||||
|
seed_backup_status_.clear();
|
||||||
|
exportSeedPhrase([this](bool ok, bool noMnemonic, const std::string& phrase,
|
||||||
|
const std::string& error) {
|
||||||
|
if (!show_seed_backup_) return; // dialog closed while the fetch was in flight
|
||||||
|
seed_backup_loading_ = false;
|
||||||
|
if (ok) {
|
||||||
|
seed_backup_phrase_ = phrase; // working copy — wiped on close
|
||||||
|
} else if (noMnemonic) {
|
||||||
|
seed_backup_no_mnemonic_ = true;
|
||||||
|
} else {
|
||||||
|
seed_backup_status_ = error.empty() ? std::string(TR("seed_backup_load_failed")) : error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ui::material::BeginOverlayDialog(TR("seed_backup_title"), &show_seed_backup_, 540.0f, 0.94f)) {
|
||||||
|
// Dismissed (Esc / outside click / X) — scrub the revealed secret.
|
||||||
|
if (seed_backup_fetch_started_) closeAndWipe();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui::material::Type().text(ui::material::TypeStyle::H6, TR("seed_backup_title"));
|
||||||
|
ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm()));
|
||||||
|
|
||||||
|
if (state_.isLocked()) {
|
||||||
|
ImGui::TextWrapped("%s", TR("seed_backup_locked"));
|
||||||
|
} else if (seed_backup_loading_) {
|
||||||
|
ImGui::TextWrapped("%s", TR("seed_backup_loading"));
|
||||||
|
} else if (seed_backup_no_mnemonic_) {
|
||||||
|
ImGui::TextWrapped("%s", TR("seed_backup_none"));
|
||||||
|
} else if (!seed_backup_phrase_.empty()) {
|
||||||
|
ImGui::TextWrapped("%s", TR("seed_backup_intro"));
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ui::material::Error());
|
||||||
|
ImGui::TextWrapped("%s", TR("seed_backup_warning"));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::Spacing();
|
||||||
|
ui::RenderSeedWordGrid(ui::SplitSeedWords(seed_backup_phrase_));
|
||||||
|
ImGui::Spacing();
|
||||||
|
if (!seed_backup_status_.empty()) {
|
||||||
|
ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), "%s", seed_backup_status_.c_str());
|
||||||
|
ImGui::Spacing();
|
||||||
|
}
|
||||||
|
ImGui::Separator();
|
||||||
|
if (ui::material::StyledButton(TR("seed_backup_copy"), ImVec2(120, 0))) {
|
||||||
|
copySecretToClipboard(seed_backup_phrase_);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ui::material::StyledButton(TR("seed_backup_save"), ImVec2(150, 0))) {
|
||||||
|
std::string path = util::Platform::getConfigDir() + "/dragonx-seed-backup.txt";
|
||||||
|
std::string content = seed_backup_phrase_ + "\n";
|
||||||
|
const bool ok = util::Platform::writeFileAtomically(path, content,
|
||||||
|
/*restrictPermissions=*/true);
|
||||||
|
sodium_memzero(&content[0], content.size());
|
||||||
|
seed_backup_status_ = (ok ? std::string(TR("seed_backup_saved"))
|
||||||
|
: std::string(TR("seed_backup_save_failed"))) + path;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ui::material::StyledButton(TR("seed_backup_close"), ImVec2(120, 0))) {
|
||||||
|
closeAndWipe();
|
||||||
|
ui::material::EndOverlayDialog();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ui::material::EndOverlayDialog();
|
||||||
|
return;
|
||||||
|
} else if (!seed_backup_status_.empty()) {
|
||||||
|
ImGui::TextColored(ImVec4(0.8f, 0.3f, 0.3f, 1.0f), "%s", seed_backup_status_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Separator();
|
||||||
|
if (ui::material::StyledButton(TR("seed_backup_close"), ImVec2(120, 0))) {
|
||||||
|
closeAndWipe();
|
||||||
|
}
|
||||||
ui::material::EndOverlayDialog();
|
ui::material::EndOverlayDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
22
src/app.h
22
src/app.h
@@ -295,6 +295,13 @@ public:
|
|||||||
|
|
||||||
// Wallet backup
|
// Wallet backup
|
||||||
void backupWallet(const std::string& destination, std::function<void(bool, const std::string&)> callback);
|
void backupWallet(const std::string& destination, std::function<void(bool, const std::string&)> callback);
|
||||||
|
// Export the wallet's BIP39 seed phrase (z_exportmnemonic). The callback receives
|
||||||
|
// (ok, noMnemonic, phrase, error): ok+phrase on success; noMnemonic=true when the
|
||||||
|
// wallet's seed is not mnemonic-derived (legacy wallet). Full-node only; the phrase
|
||||||
|
// is a secret and is wiped after the callback returns.
|
||||||
|
void exportSeedPhrase(std::function<void(bool ok, bool noMnemonic,
|
||||||
|
const std::string& phrase,
|
||||||
|
const std::string& error)> callback);
|
||||||
|
|
||||||
// Transaction operations
|
// Transaction operations
|
||||||
void sendTransaction(const std::string& from, const std::string& to,
|
void sendTransaction(const std::string& from, const std::string& to,
|
||||||
@@ -344,6 +351,7 @@ public:
|
|||||||
void showImportKeyDialog() { show_import_key_ = true; }
|
void showImportKeyDialog() { show_import_key_ = true; }
|
||||||
void showExportKeyDialog() { show_export_key_ = true; }
|
void showExportKeyDialog() { show_export_key_ = true; }
|
||||||
void showBackupDialog() { show_backup_ = true; }
|
void showBackupDialog() { show_backup_ = true; }
|
||||||
|
void showSeedBackupDialog() { show_seed_backup_ = true; }
|
||||||
void showAboutDialog() { show_about_ = true; }
|
void showAboutDialog() { show_about_ = true; }
|
||||||
|
|
||||||
// Legacy tab compat — maps int to NavPage
|
// Legacy tab compat — maps int to NavPage
|
||||||
@@ -599,6 +607,9 @@ private:
|
|||||||
// variants); derives via deriveChatIdentityFromSecret and wipes the secret. No-op when the
|
// variants); derives via deriveChatIdentityFromSecret and wipes the secret. No-op when the
|
||||||
// feature is off, already provisioned, in flight, or unavailable.
|
// feature is off, already provisioned, in flight, or unavailable.
|
||||||
void maybeProvisionChatIdentity();
|
void maybeProvisionChatIdentity();
|
||||||
|
// One-time nudge: on a full-node wallet that has a mnemonic, remind the user (once per
|
||||||
|
// install) to back up their seed phrase. Cheap early-outs keep it idle until it can act.
|
||||||
|
void maybeRemindSeedBackup();
|
||||||
void provisionChatIdentityFromSecret(std::string secret);
|
void provisionChatIdentityFromSecret(std::string secret);
|
||||||
std::string chatReplyZaddr(); // a stable (persisted) wallet z-addr for chat
|
std::string chatReplyZaddr(); // a stable (persisted) wallet z-addr for chat
|
||||||
std::string generateChatLocalId(const char* prefix, int numBytes) const; // unique echo id / cid
|
std::string generateChatLocalId(const char* prefix, int numBytes) const; // unique echo id / cid
|
||||||
@@ -656,6 +667,16 @@ private:
|
|||||||
bool show_import_key_ = false;
|
bool show_import_key_ = false;
|
||||||
bool show_export_key_ = false;
|
bool show_export_key_ = false;
|
||||||
bool show_backup_ = false;
|
bool show_backup_ = false;
|
||||||
|
bool show_seed_backup_ = false;
|
||||||
|
|
||||||
|
// Seed-phrase backup dialog state. seed_backup_phrase_ holds a SECRET (the revealed
|
||||||
|
// mnemonic) and is wiped with sodium_memzero when the dialog closes.
|
||||||
|
std::string seed_backup_phrase_;
|
||||||
|
std::string seed_backup_status_;
|
||||||
|
bool seed_backup_fetch_started_ = false;
|
||||||
|
bool seed_backup_loading_ = false;
|
||||||
|
bool seed_backup_no_mnemonic_ = false;
|
||||||
|
bool seed_backup_reminder_in_flight_ = false; // guards the one-time backup nudge probe
|
||||||
|
|
||||||
// Embedded daemon state
|
// Embedded daemon state
|
||||||
bool use_embedded_daemon_ = wallet::supportsEmbeddedDaemon(wallet::currentWalletCapabilities());
|
bool use_embedded_daemon_ = wallet::supportsEmbeddedDaemon(wallet::currentWalletCapabilities());
|
||||||
@@ -918,6 +939,7 @@ private:
|
|||||||
void renderImportKeyDialog();
|
void renderImportKeyDialog();
|
||||||
void renderExportKeyDialog();
|
void renderExportKeyDialog();
|
||||||
void renderBackupDialog();
|
void renderBackupDialog();
|
||||||
|
void renderSeedBackupDialog(); // full-node "Back up seed phrase" modal (z_exportmnemonic)
|
||||||
void renderFirstRunWizard();
|
void renderFirstRunWizard();
|
||||||
void renderLockScreen();
|
void renderLockScreen();
|
||||||
void renderEncryptWalletDialog();
|
void renderEncryptWalletDialog();
|
||||||
|
|||||||
@@ -2863,6 +2863,69 @@ void App::importPrivateKey(const std::string& rawKey, std::function<void(bool, c
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::exportSeedPhrase(std::function<void(bool, bool, const std::string&, const std::string&)> callback)
|
||||||
|
{
|
||||||
|
if (!state_.connected || !rpc_ || !worker_) {
|
||||||
|
if (callback) callback(false, false, std::string(), "Not connected to the daemon.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
worker_->post([this, callback]() -> rpc::RPCWorker::MainCb {
|
||||||
|
std::string phrase; // SECRET — wiped after the callback runs
|
||||||
|
std::string err;
|
||||||
|
bool ok = false;
|
||||||
|
bool noMnemonic = false;
|
||||||
|
rpc::RPCClient::TraceScope trace("Settings / Export seed phrase");
|
||||||
|
try {
|
||||||
|
auto response = rpc_->call("z_exportmnemonic");
|
||||||
|
if (response.contains("mnemonic") && response["mnemonic"].is_string()) {
|
||||||
|
auto& m = response["mnemonic"].get_ref<std::string&>();
|
||||||
|
phrase = m;
|
||||||
|
if (!m.empty()) sodium_memzero(&m[0], m.size()); // scrub the json's own copy
|
||||||
|
ok = !phrase.empty();
|
||||||
|
} else {
|
||||||
|
err = "The daemon returned no seed phrase.";
|
||||||
|
}
|
||||||
|
} catch (const rpc::RpcError& e) {
|
||||||
|
err = e.what();
|
||||||
|
// The daemon errors with this specific message when the wallet's seed is not
|
||||||
|
// mnemonic-derived (a legacy wallet). Any other error (e.g. locked) stays generic.
|
||||||
|
if (err.find("not derived from a mnemonic") != std::string::npos)
|
||||||
|
noMnemonic = true;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
err = e.what();
|
||||||
|
}
|
||||||
|
return [callback, ok, noMnemonic, phrase = std::move(phrase), err]() mutable {
|
||||||
|
if (callback) callback(ok, noMnemonic, phrase, err);
|
||||||
|
if (!phrase.empty()) sodium_memzero(&phrase[0], phrase.size());
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// One-time nudge to back up the seed phrase on a mnemonic-backed full-node wallet. Fires at
|
||||||
|
// most one z_exportmnemonic probe per install; the phrase is not retained (the probe only
|
||||||
|
// learns whether a mnemonic exists). Legacy (non-mnemonic) wallets are not nagged.
|
||||||
|
void App::maybeRemindSeedBackup()
|
||||||
|
{
|
||||||
|
if (lite_wallet_) return; // lite has its own seed UX
|
||||||
|
if (!settings_ || settings_->getSeedBackupReminded()) return;
|
||||||
|
if (!state_.connected || !state_.encryption_state_known) return;
|
||||||
|
if (state_.isLocked()) return; // wait until unlocked to read it
|
||||||
|
if (seed_backup_reminder_in_flight_) return;
|
||||||
|
seed_backup_reminder_in_flight_ = true;
|
||||||
|
exportSeedPhrase([this](bool ok, bool noMnemonic, const std::string& /*phrase*/,
|
||||||
|
const std::string& /*error*/) {
|
||||||
|
seed_backup_reminder_in_flight_ = false;
|
||||||
|
// Only mark "reminded" once we have a definitive answer, so a transient failure retries
|
||||||
|
// next session rather than silently suppressing the reminder forever.
|
||||||
|
if (ok || noMnemonic) {
|
||||||
|
settings_->setSeedBackupReminded(true);
|
||||||
|
settings_->save();
|
||||||
|
}
|
||||||
|
if (ok)
|
||||||
|
ui::Notifications::instance().info(TR("seed_backup_reminder"), 12.0f);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void App::backupWallet(const std::string& destination, std::function<void(bool, const std::string&)> callback)
|
void App::backupWallet(const std::string& destination, std::function<void(bool, const std::string&)> callback)
|
||||||
{
|
{
|
||||||
if (!state_.connected || !rpc_) {
|
if (!state_.connected || !rpc_) {
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ bool Settings::load(const std::string& path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadScalar(j, "wizard_completed", wizard_completed_);
|
loadScalar(j, "wizard_completed", wizard_completed_);
|
||||||
|
loadScalar(j, "seed_backup_reminded", seed_backup_reminded_);
|
||||||
loadScalar(j, "auto_lock_timeout", auto_lock_timeout_);
|
loadScalar(j, "auto_lock_timeout", auto_lock_timeout_);
|
||||||
loadScalar(j, "unlock_duration", unlock_duration_);
|
loadScalar(j, "unlock_duration", unlock_duration_);
|
||||||
loadScalar(j, "pin_enabled", pin_enabled_);
|
loadScalar(j, "pin_enabled", pin_enabled_);
|
||||||
@@ -432,6 +433,7 @@ bool Settings::save(const std::string& path)
|
|||||||
j["address_meta"] = meta_obj;
|
j["address_meta"] = meta_obj;
|
||||||
}
|
}
|
||||||
j["wizard_completed"] = wizard_completed_;
|
j["wizard_completed"] = wizard_completed_;
|
||||||
|
j["seed_backup_reminded"] = seed_backup_reminded_;
|
||||||
j["auto_lock_timeout"] = auto_lock_timeout_;
|
j["auto_lock_timeout"] = auto_lock_timeout_;
|
||||||
j["unlock_duration"] = unlock_duration_;
|
j["unlock_duration"] = unlock_duration_;
|
||||||
j["pin_enabled"] = pin_enabled_;
|
j["pin_enabled"] = pin_enabled_;
|
||||||
|
|||||||
@@ -246,6 +246,10 @@ public:
|
|||||||
bool getWizardCompleted() const { return wizard_completed_; }
|
bool getWizardCompleted() const { return wizard_completed_; }
|
||||||
void setWizardCompleted(bool v) { wizard_completed_ = v; }
|
void setWizardCompleted(bool v) { wizard_completed_ = v; }
|
||||||
|
|
||||||
|
// Whether the one-time "back up your seed phrase" reminder has already been shown.
|
||||||
|
bool getSeedBackupReminded() const { return seed_backup_reminded_; }
|
||||||
|
void setSeedBackupReminded(bool v) { seed_backup_reminded_ = v; }
|
||||||
|
|
||||||
// Security — auto-lock timeout (seconds; 0 = disabled)
|
// Security — auto-lock timeout (seconds; 0 = disabled)
|
||||||
int getAutoLockTimeout() const { return auto_lock_timeout_; }
|
int getAutoLockTimeout() const { return auto_lock_timeout_; }
|
||||||
void setAutoLockTimeout(int seconds) { auto_lock_timeout_ = seconds; }
|
void setAutoLockTimeout(int seconds) { auto_lock_timeout_ = seconds; }
|
||||||
@@ -444,6 +448,7 @@ private:
|
|||||||
std::set<std::string> favorite_addresses_;
|
std::set<std::string> favorite_addresses_;
|
||||||
std::map<std::string, AddressMeta> address_meta_;
|
std::map<std::string, AddressMeta> address_meta_;
|
||||||
bool wizard_completed_ = false;
|
bool wizard_completed_ = false;
|
||||||
|
bool seed_backup_reminded_ = false;
|
||||||
int auto_lock_timeout_ = 900; // 15 minutes
|
int auto_lock_timeout_ = 900; // 15 minutes
|
||||||
int unlock_duration_ = 600; // 10 minutes
|
int unlock_duration_ = 600; // 10 minutes
|
||||||
bool pin_enabled_ = false;
|
bool pin_enabled_ = false;
|
||||||
|
|||||||
@@ -212,6 +212,11 @@ std::vector<std::string> EmbeddedDaemon::getChainParams()
|
|||||||
"-addnode=node4.dragonx.is",
|
"-addnode=node4.dragonx.is",
|
||||||
"-experimentalfeatures",
|
"-experimentalfeatures",
|
||||||
"-developerencryptwallet",
|
"-developerencryptwallet",
|
||||||
|
// Create fresh wallets from a BIP39 mnemonic so their 24-word phrase can be
|
||||||
|
// exported (z_exportmnemonic) and is portable to SDXLite/ObsidianDragonLite.
|
||||||
|
// The daemon reads this ONLY inside GenerateNewSeed() when a wallet has no seed
|
||||||
|
// yet, so it is inert on existing wallets — safe to pass unconditionally.
|
||||||
|
"-usemnemonic=1",
|
||||||
dbcache_arg
|
dbcache_arg
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1304,8 +1304,10 @@ void RenderSettingsPage(App* app) {
|
|||||||
naturalW += ImGui::CalcTextSize(r1[i]).x + btnPadX;
|
naturalW += ImGui::CalcTextSize(r1[i]).x + btnPadX;
|
||||||
float wizW = showFullNodeLifecycleActions ? ImGui::CalcTextSize(wizLabel).x + btnPadX : 0.0f;
|
float wizW = showFullNodeLifecycleActions ? ImGui::CalcTextSize(wizLabel).x + btnPadX : 0.0f;
|
||||||
float bsW = showFullNodeLifecycleActions ? ImGui::CalcTextSize(bsLabel).x + btnPadX : 0.0f;
|
float bsW = showFullNodeLifecycleActions ? ImGui::CalcTextSize(bsLabel).x + btnPadX : 0.0f;
|
||||||
|
// Full-node-only "Seed phrase" button trails the Backup button (see below).
|
||||||
|
float seedW = showFullNodeLifecycleActions ? ImGui::CalcTextSize(TR("seed_backup_button")).x + btnPadX : 0.0f;
|
||||||
float totalW = naturalW + sp * 5;
|
float totalW = naturalW + sp * 5;
|
||||||
if (showFullNodeLifecycleActions) totalW += wizW + bsW + sp * 2;
|
if (showFullNodeLifecycleActions) totalW += wizW + bsW + seedW + sp * 3;
|
||||||
|
|
||||||
float scale = (totalW > contentW) ? contentW / totalW : 1.0f;
|
float scale = (totalW > contentW) ? contentW / totalW : 1.0f;
|
||||||
if (scale < 1.0f) ImGui::SetWindowFontScale(scale);
|
if (scale < 1.0f) ImGui::SetWindowFontScale(scale);
|
||||||
@@ -1326,6 +1328,13 @@ void RenderSettingsPage(App* app) {
|
|||||||
if (TactileButton(r1[3], ImVec2(0, 0), btnFont))
|
if (TactileButton(r1[3], ImVec2(0, 0), btnFont))
|
||||||
app->showBackupDialog();
|
app->showBackupDialog();
|
||||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", t1[3]);
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", t1[3]);
|
||||||
|
if (showFullNodeLifecycleActions) {
|
||||||
|
// Back up the wallet's BIP39 seed phrase (full-node mnemonic wallets).
|
||||||
|
ImGui::SameLine(0, scaledSp);
|
||||||
|
if (TactileButton(TR("seed_backup_button"), ImVec2(0, 0), btnFont))
|
||||||
|
app->showSeedBackupDialog();
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_seed_backup"));
|
||||||
|
}
|
||||||
ImGui::SameLine(0, scaledSp);
|
ImGui::SameLine(0, scaledSp);
|
||||||
if (TactileButton(r1[4], ImVec2(0, 0), btnFont))
|
if (TactileButton(r1[4], ImVec2(0, 0), btnFont))
|
||||||
ExportTransactionsDialog::show();
|
ExportTransactionsDialog::show();
|
||||||
|
|||||||
46
src/ui/windows/seed_display.h
Normal file
46
src/ui/windows/seed_display.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Shared helpers for rendering a BIP39 seed phrase as a numbered word grid and for
|
||||||
|
// splitting a whitespace-separated phrase into words. Used by the full-node seed-phrase
|
||||||
|
// backup dialog and the "migrate to a seed wallet" flow so both present the phrase the
|
||||||
|
// same way the lite welcome wizard does.
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
namespace dragonx {
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
// Split a whitespace-separated seed phrase into its individual words.
|
||||||
|
inline std::vector<std::string> SplitSeedWords(const std::string& phrase)
|
||||||
|
{
|
||||||
|
std::vector<std::string> words;
|
||||||
|
std::string cur;
|
||||||
|
for (char c : phrase) {
|
||||||
|
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
||||||
|
if (!cur.empty()) { words.push_back(cur); cur.clear(); }
|
||||||
|
} else {
|
||||||
|
cur.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!cur.empty()) words.push_back(cur);
|
||||||
|
return words;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the words as a numbered 4-column grid (matches the lite welcome wizard layout).
|
||||||
|
inline void RenderSeedWordGrid(const std::vector<std::string>& words, float colSpacingPx = 130.0f)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < words.size(); ++i) {
|
||||||
|
char cell[96];
|
||||||
|
std::snprintf(cell, sizeof(cell), "%2zu. %s", i + 1, words[i].c_str());
|
||||||
|
ImGui::TextUnformatted(cell);
|
||||||
|
if ((i % 4) != 3 && i + 1 < words.size())
|
||||||
|
ImGui::SameLine(((i % 4) + 1) * colSpacingPx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace dragonx
|
||||||
@@ -233,6 +233,22 @@ void I18n::loadBuiltinEnglish()
|
|||||||
strings_["chat_toast_compose_failed"] = "Could not compose the message (too long?).";
|
strings_["chat_toast_compose_failed"] = "Could not compose the message (too long?).";
|
||||||
strings_["chat_toast_request_compose_failed"] = "Could not compose the contact request (invalid address / text?).";
|
strings_["chat_toast_request_compose_failed"] = "Could not compose the contact request (invalid address / text?).";
|
||||||
strings_["chat_toast_request_queued"] = "Contact request queued.";
|
strings_["chat_toast_request_queued"] = "Contact request queued.";
|
||||||
|
// Seed-phrase backup (full-node)
|
||||||
|
strings_["seed_backup_button"] = "Seed phrase";
|
||||||
|
strings_["tt_seed_backup"] = "Show and back up your wallet's 24-word recovery seed phrase";
|
||||||
|
strings_["seed_backup_title"] = "Back up seed phrase";
|
||||||
|
strings_["seed_backup_intro"] = "These words are the master backup of your wallet. Anyone who has them controls your funds.";
|
||||||
|
strings_["seed_backup_warning"] = "Write them down in order, store them offline, and never share or photograph them. If you lose them, your funds cannot be recovered.";
|
||||||
|
strings_["seed_backup_loading"] = "Loading your seed phrase…";
|
||||||
|
strings_["seed_backup_locked"] = "Unlock your wallet to reveal its seed phrase.";
|
||||||
|
strings_["seed_backup_none"] = "This wallet predates seed phrases, so it has no 24-word phrase. Back it up with \"Backup wallet\" or by exporting your private keys instead.";
|
||||||
|
strings_["seed_backup_load_failed"] = "Could not load the seed phrase.";
|
||||||
|
strings_["seed_backup_copy"] = "Copy";
|
||||||
|
strings_["seed_backup_save"] = "Save to file…";
|
||||||
|
strings_["seed_backup_saved"] = "Saved to ";
|
||||||
|
strings_["seed_backup_save_failed"] = "Could not write ";
|
||||||
|
strings_["seed_backup_close"] = "Close";
|
||||||
|
strings_["seed_backup_reminder"] = "Your wallet has a 24-word recovery seed phrase. Back it up now in Settings → Node & Security.";
|
||||||
strings_["contacts_search_placeholder"] = "Search contacts...";
|
strings_["contacts_search_placeholder"] = "Search contacts...";
|
||||||
strings_["contacts_search_no_match"] = "No matching contacts";
|
strings_["contacts_search_no_match"] = "No matching contacts";
|
||||||
strings_["address_book_confirm_delete"] = "Confirm delete?";
|
strings_["address_book_confirm_delete"] = "Confirm delete?";
|
||||||
|
|||||||
Reference in New Issue
Block a user