feat(fullnode): add "Repair Wallet" (-zapwallettxes=2) to Settings
When a note's stored record is corrupt or its tx isn't in the canonical chain, z_sendmany fails to build a valid sapling spend proof even after a full -rescan, because a plain rescan replays witnesses but keeps the existing tx/note records. The zcashd repair for this is -zapwallettxes=2, which deletes all wallet tx/note records and rebuilds them from the chain (keys/addresses preserved). Adds a RepairWallet lifecycle operation that mirrors the existing -rescan plumbing (one-shot zapOnNextStart flag on the embedded daemon; -zapwallettxes=2 implies and supersedes -rescan), an App::repairWallet() that reuses the rescan status UI (so the status bar + warmup-end completion detection apply), and a confirmed "Repair Wallet" button + dialog in Settings → node maintenance (embedded daemon only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
41
src/app.cpp
41
src/app.cpp
@@ -2989,6 +2989,47 @@ void App::rescanBlockchain()
|
||||
});
|
||||
}
|
||||
|
||||
void App::repairWallet()
|
||||
{
|
||||
if (!supportsFullNodeLifecycleActions()) {
|
||||
ui::Notifications::instance().warning("Full-node lifecycle actions are unavailable in lite build");
|
||||
return;
|
||||
}
|
||||
|
||||
auto decision = daemon::DaemonController::evaluateLifecycleOperation(
|
||||
daemon::DaemonController::LifecycleOperation::RepairWallet,
|
||||
isUsingEmbeddedDaemon(), daemon_controller_ != nullptr, isEmbeddedDaemonRunning());
|
||||
if (!decision.allowed) {
|
||||
ui::Notifications::instance().warning(decision.warning);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOGF("[App] Starting wallet repair (-zapwallettxes=2) - stopping daemon first\n");
|
||||
ui::Notifications::instance().info("Restarting daemon with -zapwallettxes=2 (wallet repair)...");
|
||||
|
||||
// -zapwallettxes=2 deletes and rebuilds every wallet tx/note record, then rescans the whole
|
||||
// chain — so reuse the rescan status UI (status bar + warmup-end completion detection). Same
|
||||
// confirmed-active gating as rescan: the first poll may still reach the pre-restart daemon.
|
||||
state_.sync.rescanning = true;
|
||||
rescan_confirmed_active_ = false;
|
||||
state_.sync.rescan_progress = 0.0f;
|
||||
state_.sync.rescan_status = decision.status;
|
||||
transactions_dirty_ = true;
|
||||
last_tx_block_height_ = -1;
|
||||
invalidateShieldedHistoryScanProgress(true);
|
||||
|
||||
// Set the zap flag BEFORE stopping so it's ready when we restart
|
||||
daemon_controller_->prepareLifecycleOperation(decision, settings_.get());
|
||||
DEBUG_LOGF("[App] Wallet-repair flag set, zapOnNextStart=%d\n", daemon_controller_->zapOnNextStart() ? 1 : 0);
|
||||
|
||||
async_tasks_.submit(decision.taskName, [this, decision](const util::AsyncTaskManager::Token& token) {
|
||||
DEBUG_LOGF("[App] Stopping daemon for wallet repair...\n");
|
||||
AppDaemonLifecycleRuntime runtime(*this);
|
||||
daemon::AsyncLifecycleTaskContext context(token, shutting_down_);
|
||||
daemon_controller_->executeLifecycleOperation(decision, runtime, context);
|
||||
});
|
||||
}
|
||||
|
||||
void App::deleteBlockchainData()
|
||||
{
|
||||
if (!supportsFullNodeLifecycleActions()) {
|
||||
|
||||
Reference in New Issue
Block a user