feat(wallet): detect a corrupt target wallet on switch + offer -salvagewallet repair

When a switch fails, the failure modal now distinguishes a CORRUPT target wallet
from other failures and offers a one-click repair:
- The switch worker watermarks the node's captured console output before the
  start and, if the node dies in init, scans this start's output for a corruption
  signature ("Failed to rename … .bak" / "salvage failed" / "wallet.dat corrupt" /
  "Error loading wallet") → sets switch_wallet_corrupt_.
- The Failed modal then shows an accurate "this wallet appears corrupt" message
  (instead of the generic "Couldn't open that wallet") plus a "Try to repair
  (salvage)" button that retries the switch with the target node started under
  -salvagewallet (recovers readable keypairs; implies -rescan).
- EmbeddedDaemon::setSalvageOnNextStart (one-shot, precedence salvage > zap >
  rescan) + controller forwarder; switchToWallet gains a salvage arg.

Salvage operates only on the corrupt target (never the good wallet, no fund
movement). Adversarially verified: output isolation, one-shot lifecycle, state
handling, re-entrancy, no success-path regression.

i18n (EN + 8 languages) + 2 new CJK glyphs baked into the subset font.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-15 23:40:33 -05:00
parent faf77de9ce
commit d9911a9bc9
17 changed files with 92 additions and 14 deletions

View File

@@ -1294,6 +1294,8 @@
"sweep_to": "Gefegt an:",
"sweep_toggle": "In meine Wallet fegen (Schlüssel nicht behalten)",
"sweep_tx": "Transaktion:",
"switch_corrupt_body": "Diese Wallet scheint beschädigt zu sein der Knoten konnte sie nicht öffnen. Aus einem Backup wiederherstellen, neu erstellen oder eine Reparatur versuchen.",
"switch_corrupt_repair": "Reparatur versuchen (Salvage)",
"switch_progress_background": "Im Hintergrund fortsetzen",
"switch_progress_default_wallet": "Standard-Wallet",
"switch_progress_elapsed": "Vergangen",

View File

@@ -1294,6 +1294,8 @@
"sweep_to": "Barrido a:",
"sweep_toggle": "Barrer a mi monedero (no conservar la clave)",
"sweep_tx": "Transacción:",
"switch_corrupt_body": "Esta cartera parece dañada: el nodo no pudo abrirla. Restáurala desde una copia de seguridad, vuelve a crearla o intenta repararla.",
"switch_corrupt_repair": "Intentar reparar (salvage)",
"switch_progress_background": "Continuar en segundo plano",
"switch_progress_default_wallet": "Cartera predeterminada",
"switch_progress_elapsed": "Transcurrido",

View File

@@ -1294,6 +1294,8 @@
"sweep_to": "Balayé vers :",
"sweep_toggle": "Balayer vers mon portefeuille (ne pas conserver la clé)",
"sweep_tx": "Transaction :",
"switch_corrupt_body": "Ce portefeuille semble corrompu — le nœud n'a pas pu l'ouvrir. Restaurez-le depuis une sauvegarde, recréez-le ou tentez de le réparer.",
"switch_corrupt_repair": "Tenter une réparation (salvage)",
"switch_progress_background": "Continuer en arrière-plan",
"switch_progress_default_wallet": "Portefeuille par défaut",
"switch_progress_elapsed": "Écoulé",

View File

@@ -1294,6 +1294,8 @@
"sweep_to": "集約先:",
"sweep_toggle": "ウォレットに集約(鍵は保持しない)",
"sweep_tx": "取引:",
"switch_corrupt_body": "このウォレットは破損しているようです。ノードが開けませんでした。バックアップから復元するか、作り直すか、修復を試してください。",
"switch_corrupt_repair": "修復を試すsalvage",
"switch_progress_background": "バックグラウンドで続行",
"switch_progress_default_wallet": "既定のウォレット",
"switch_progress_elapsed": "経過",

View File

@@ -1294,6 +1294,8 @@
"sweep_to": "쓸어담은 주소:",
"sweep_toggle": "내 지갑으로 쓸어담기 (키 보관 안 함)",
"sweep_tx": "거래:",
"switch_corrupt_body": "이 지갑이 손상된 것 같습니다. 노드가 열 수 없습니다. 백업에서 복원하거나 다시 만들거나 복구를 시도하세요.",
"switch_corrupt_repair": "복구 시도(salvage)",
"switch_progress_background": "백그라운드에서 계속",
"switch_progress_default_wallet": "기본 지갑",
"switch_progress_elapsed": "경과",

View File

@@ -1294,6 +1294,8 @@
"sweep_to": "Varrido para:",
"sweep_toggle": "Varrer para minha carteira (não manter a chave)",
"sweep_tx": "Transação:",
"switch_corrupt_body": "Esta carteira parece corrompida — o nó não conseguiu abri-la. Restaure de um backup, recrie-a ou tente repará-la.",
"switch_corrupt_repair": "Tentar reparar (salvage)",
"switch_progress_background": "Continuar em segundo plano",
"switch_progress_default_wallet": "Carteira padrão",
"switch_progress_elapsed": "Decorrido",

View File

@@ -1294,6 +1294,8 @@
"sweep_to": "Переведено на:",
"sweep_toggle": "Перевести в мой кошелёк (не сохранять ключ)",
"sweep_tx": "Транзакция:",
"switch_corrupt_body": "Похоже, этот кошелёк повреждён — узел не смог его открыть. Восстановите из резервной копии, создайте заново или попробуйте восстановить.",
"switch_corrupt_repair": "Попробовать восстановить (salvage)",
"switch_progress_background": "Продолжить в фоне",
"switch_progress_default_wallet": "Кошелёк по умолчанию",
"switch_progress_elapsed": "Прошло",

View File

@@ -1294,6 +1294,8 @@
"sweep_to": "归集到:",
"sweep_toggle": "归集到我的钱包(不保留密钥)",
"sweep_tx": "交易:",
"switch_corrupt_body": "此钱包似乎已损坏——节点无法打开它。请从备份恢复、重新创建,或尝试修复。",
"switch_corrupt_repair": "尝试修复salvage",
"switch_progress_background": "在后台继续",
"switch_progress_default_wallet": "默认钱包",
"switch_progress_elapsed": "已用时",