feat(node): one-click "Restore original wallet" after a daemon auto-recovery

Adds the restore action to the wallet-auto-recovery warning: undo the daemon's
salvage by swapping the untouched original (wallet.<ts>.bak) back over the
salvaged copy and clearing the stale BDB env that triggered the false recovery,
then restarting. Modeled on beginAdoptSeedWallet (stop daemon → file ops →
restart on a worker; result pumped to the main thread for notifications).

Safety (fund-adjacent file ops on a real wallet — copy/rename only, never delete
user data):
- picks the newest wallet.<unixtime>.bak via the pure, unit-tested
  newestWalletSalvageBak(); aborts if none.
- verifies the .bak is a real Berkeley DB (probeWalletFile) before touching
  anything — won't overwrite a working wallet with a bad backup.
- stops the daemon first (stopDaemonForWalletSwitch) so wallet.dat is released.
- moves the salvaged copy aside to wallet.dat.salvaged-<ts>.dat (kept), COPIES
  the .bak into place (the .bak stays), moves database/ aside to
  database.pre-restore-<ts>.bak (kept), and drops only the transient __db.*
  BDB region files. Rolls back the move if the copy fails.
- relaunches the node even on failure so it's never left down.

The warning dialog now offers Restore original wallet / Open data folder /
Keep salvaged copy. Full-node only; lite-safe. Build clean, suite green (1/1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-10 00:45:47 -05:00
parent 3b7423f3a1
commit 384d64ea5d
4 changed files with 149 additions and 2 deletions

View File

@@ -830,6 +830,7 @@ void App::update()
// Pick up progress/result from a running seed-wallet migration (create/sweep/adopt).
pumpSeedMigration();
pumpWalletRestore();
// While confirming the sweep, poll the tx confirmations + legacy balance every ~5s.
if (show_seed_migration_ && seed_migration_step_ == SeedMigrationStep::Confirming) {
seed_migration_poll_timer_ -= ImGui::GetIO().DeltaTime;
@@ -4302,8 +4303,14 @@ void App::renderWalletRecoveredDialog()
ImGui::Dummy(ImVec2(0, ui::Layout::spacingSm()));
ImGui::TextWrapped("%s", TR("wallet_recovered_body"));
ImGui::Dummy(ImVec2(0, ui::Layout::spacingMd()));
if (ui::material::TactileButton(TR("wallet_recovered_open_folder"), ImVec2(240.0f * dp, 0))) {
util::Platform::openFolder(util::Platform::getDragonXDataDir());
// Primary: one-click restore of the untouched original (stops the node, swaps the .bak back over the
// salvaged copy, clears the stale BDB env, restarts). Copy/rename-only — nothing is deleted.
if (ui::material::TactileButton(TR("wallet_recovered_restore"), ImVec2(260.0f * dp, 0))) {
restoreOriginalWallet(); // clears show_wallet_recovered_dialog_
}
ImGui::SameLine();
if (ui::material::TactileButton(TR("wallet_recovered_open_folder"), ImVec2(200.0f * dp, 0))) {
util::Platform::openFolder(util::Platform::getDragonXDataDir()); // manual restore instead
}
ImGui::SameLine();
if (ui::material::TactileButton(TR("wallet_recovered_dismiss"), ImVec2(150.0f * dp, 0))) {