feat: RPC caching, background decrypt import, fast-lane peers, mining fix
RPC client: - Add call() overload with per-call timeout parameter - z_exportwallet uses 300s, z_importwallet uses 1200s timeout Decrypt wallet (app_security.cpp, app.cpp): - Show per-step and overall elapsed timers during decrypt flow - Reduce dialog to 5 steps; close before key import begins - Run z_importwallet on detached background thread - Add pulsing "Importing keys..." status bar indicator - Report success/failure via notifications instead of dialog RPC caching (app_network.cpp, app.h): - Cache z_viewtransaction results in viewtx_cache_ across refresh cycles - Skip RPC calls for already-cached txids (biggest perf win) - Build confirmed_tx_cache_ for deeply-confirmed transactions - Clear all caches on disconnect - Remove unused refreshTransactions() dead code Peers (app_network.cpp, peers_tab.cpp): - Route refreshPeerInfo() through fast_worker_ to avoid head-of-line blocking - Replace footer "Refresh Peers" button with ICON_MD_REFRESH in toggle header - Refresh button triggers both peer list and full blockchain data refresh Mining (mining_tab.cpp): - Allow pool mining toggle when blockchain is not synced - Pool mining only needs xmrig, not local daemon sync
This commit is contained in:
@@ -1021,6 +1021,8 @@ void App::renderDecryptWalletDialog() {
|
||||
decrypt_step_ = 0;
|
||||
decrypt_in_progress_ = true;
|
||||
decrypt_status_ = "Unlocking wallet...";
|
||||
decrypt_overall_start_time_ = std::chrono::steady_clock::now();
|
||||
decrypt_step_start_time_ = decrypt_overall_start_time_;
|
||||
|
||||
// Run entire decrypt flow on worker thread
|
||||
if (worker_) {
|
||||
@@ -1040,6 +1042,7 @@ void App::renderDecryptWalletDialog() {
|
||||
// Update step on main thread
|
||||
return [this]() {
|
||||
decrypt_step_ = 1;
|
||||
decrypt_step_start_time_ = std::chrono::steady_clock::now();
|
||||
decrypt_status_ = "Exporting wallet keys...";
|
||||
|
||||
// Continue with step 2
|
||||
@@ -1050,7 +1053,7 @@ void App::renderDecryptWalletDialog() {
|
||||
std::string exportPath = dataDir + exportFile;
|
||||
|
||||
try {
|
||||
rpc_->call("z_exportwallet", {exportFile});
|
||||
rpc_->call("z_exportwallet", {exportFile}, 300L);
|
||||
} catch (const std::exception& e) {
|
||||
std::string err = e.what();
|
||||
return [this, err]() {
|
||||
@@ -1062,6 +1065,7 @@ void App::renderDecryptWalletDialog() {
|
||||
|
||||
return [this, exportPath]() {
|
||||
decrypt_step_ = 2;
|
||||
decrypt_step_start_time_ = std::chrono::steady_clock::now();
|
||||
decrypt_status_ = "Stopping daemon...";
|
||||
|
||||
// Continue with step 3
|
||||
@@ -1077,6 +1081,7 @@ void App::renderDecryptWalletDialog() {
|
||||
|
||||
return [this, exportPath]() {
|
||||
decrypt_step_ = 3;
|
||||
decrypt_step_start_time_ = std::chrono::steady_clock::now();
|
||||
decrypt_status_ = "Backing up encrypted wallet...";
|
||||
|
||||
// Continue with step 4 (rename)
|
||||
@@ -1100,6 +1105,7 @@ void App::renderDecryptWalletDialog() {
|
||||
|
||||
return [this, exportPath]() {
|
||||
decrypt_step_ = 4;
|
||||
decrypt_step_start_time_ = std::chrono::steady_clock::now();
|
||||
decrypt_status_ = "Restarting daemon...";
|
||||
|
||||
auto restartAndImport = [this, exportPath]() {
|
||||
@@ -1136,28 +1142,50 @@ void App::renderDecryptWalletDialog() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update step on main thread
|
||||
// Update step on main thread — close dialog, import in background
|
||||
if (worker_) {
|
||||
worker_->post([this]() -> rpc::RPCWorker::MainCb {
|
||||
return [this]() {
|
||||
decrypt_step_ = 5;
|
||||
decrypt_status_ = "Importing keys (this may take a while)...";
|
||||
// Close the decrypt dialog — user can use the wallet now
|
||||
decrypt_in_progress_ = false;
|
||||
show_decrypt_dialog_ = false;
|
||||
decrypt_import_active_ = true;
|
||||
|
||||
// Mark rescanning so status bar picks it up immediately
|
||||
state_.sync.rescanning = true;
|
||||
state_.sync.rescan_progress = 0.0f;
|
||||
|
||||
// Clear encryption state early — vault/PIN removed now,
|
||||
// wallet file is already unencrypted
|
||||
if (vault_ && vault_->hasVault()) {
|
||||
vault_->removeVault();
|
||||
}
|
||||
if (settings_ && settings_->getPinEnabled()) {
|
||||
settings_->setPinEnabled(false);
|
||||
settings_->save();
|
||||
}
|
||||
|
||||
ui::Notifications::instance().info(
|
||||
"Importing keys & rescanning blockchain — wallet is usable while this runs",
|
||||
8.0f);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Step 6: Import wallet (use full path)
|
||||
// Step 6: Import wallet in background (use full path)
|
||||
// Use 20-minute timeout — import + rescan can be very slow
|
||||
try {
|
||||
rpc_->call("z_importwallet", {exportPath});
|
||||
rpc_->call("z_importwallet", {exportPath}, 1200L);
|
||||
} catch (const std::exception& e) {
|
||||
std::string err = e.what();
|
||||
if (worker_) {
|
||||
worker_->post([this, err]() -> rpc::RPCWorker::MainCb {
|
||||
return [this, err]() {
|
||||
decrypt_in_progress_ = false;
|
||||
decrypt_status_ = "Import failed: " + err +
|
||||
"\nYour encrypted wallet backup is at wallet.dat.encrypted.bak";
|
||||
decrypt_phase_ = 3;
|
||||
decrypt_import_active_ = false;
|
||||
ui::Notifications::instance().error(
|
||||
"Key import failed: " + err +
|
||||
"\nEncrypted backup: wallet.dat.encrypted.bak",
|
||||
12.0f);
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1168,19 +1196,12 @@ void App::renderDecryptWalletDialog() {
|
||||
if (worker_) {
|
||||
worker_->post([this]() -> rpc::RPCWorker::MainCb {
|
||||
return [this]() {
|
||||
decrypt_in_progress_ = false;
|
||||
decrypt_status_ = "Wallet decrypted successfully!";
|
||||
decrypt_phase_ = 2;
|
||||
|
||||
if (vault_ && vault_->hasVault()) {
|
||||
vault_->removeVault();
|
||||
}
|
||||
if (settings_ && settings_->getPinEnabled()) {
|
||||
settings_->setPinEnabled(false);
|
||||
settings_->save();
|
||||
}
|
||||
decrypt_import_active_ = false;
|
||||
|
||||
refreshWalletEncryptionState();
|
||||
ui::Notifications::instance().success(
|
||||
"Wallet decrypted successfully! All keys imported.",
|
||||
8.0f);
|
||||
DEBUG_LOGF("[App] Wallet decrypted successfully\n");
|
||||
};
|
||||
});
|
||||
@@ -1214,11 +1235,17 @@ void App::renderDecryptWalletDialog() {
|
||||
"Exporting wallet keys",
|
||||
"Stopping daemon",
|
||||
"Backing up encrypted wallet",
|
||||
"Restarting daemon",
|
||||
"Importing keys (rescan)"
|
||||
"Restarting daemon"
|
||||
};
|
||||
const int numSteps = 6;
|
||||
const int numSteps = 5;
|
||||
|
||||
// Compute elapsed times
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto stepElapsed = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
now - decrypt_step_start_time_).count();
|
||||
auto totalElapsed = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
now - decrypt_overall_start_time_).count();
|
||||
|
||||
ImGui::Spacing();
|
||||
for (int i = 0; i < numSteps; i++) {
|
||||
ImGui::PushFont(Type().iconMed());
|
||||
@@ -1237,7 +1264,16 @@ void App::renderDecryptWalletDialog() {
|
||||
ImGui::SameLine();
|
||||
|
||||
if (i == decrypt_step_) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.9f, 0.6f, 1.0f), "%s...", stepLabels[i]);
|
||||
// Show step label with elapsed time
|
||||
int mins = (int)(stepElapsed / 60);
|
||||
int secs = (int)(stepElapsed % 60);
|
||||
if (mins > 0) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.9f, 0.6f, 1.0f),
|
||||
"%s... (%dm %02ds)", stepLabels[i], mins, secs);
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.9f, 0.6f, 1.0f),
|
||||
"%s... (%ds)", stepLabels[i], secs);
|
||||
}
|
||||
} else if (i < decrypt_step_) {
|
||||
ImGui::TextColored(ImVec4(0.6f, 0.8f, 0.6f, 1.0f), "%s", stepLabels[i]);
|
||||
} else {
|
||||
@@ -1271,8 +1307,22 @@ void App::renderDecryptWalletDialog() {
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("Please wait. The daemon is exporting keys, restarting, "
|
||||
"and re-importing. This may take several minutes.");
|
||||
|
||||
// Step-specific hints
|
||||
if (decrypt_step_ == 4) {
|
||||
ImGui::TextWrapped("Waiting for the daemon to finish starting up...");
|
||||
} else {
|
||||
ImGui::TextWrapped("Please wait. The daemon is exporting keys, restarting, "
|
||||
"and re-importing. This may take several minutes.");
|
||||
}
|
||||
|
||||
// Total elapsed
|
||||
{
|
||||
int tMins = (int)(totalElapsed / 60);
|
||||
int tSecs = (int)(totalElapsed % 60);
|
||||
ImGui::Spacing();
|
||||
ImGui::TextDisabled("Total elapsed: %dm %02ds", tMins, tSecs);
|
||||
}
|
||||
|
||||
// ---- Phase 2: Success ----
|
||||
} else if (decrypt_phase_ == 2) {
|
||||
|
||||
Reference in New Issue
Block a user