diff --git a/src/app.cpp b/src/app.cpp index 190e2cc..2783031 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -652,6 +652,7 @@ void App::update() state_.sync.building_witnesses = false; state_.sync.witness_progress = 0.0f; state_.sync.witness_remaining = 0; + witness_rebuild_total_blocks_ = 0; } // else: rescanning=false but not yet confirmed → pre-restart daemon; keep waiting. }; @@ -825,23 +826,11 @@ void App::update() } // Earlier per-tx phase: "Setting Initial Sapling Witness for tx , of ". - auto setIdx = line.find("Setting Initial Sapling Witness"); - if (setIdx != std::string::npos) { + // Its resets to 0 on every BuildWitnessCache call (re-invoked per block), so + // it's not a reliable overall counter — use it only to mark that the witness + // phase is underway; the block-walk "remaining" below drives the percentage. + if (line.find("Setting Initial Sapling Witness") != std::string::npos) { foundWitness = true; - auto ofIdx = line.find(" of ", setIdx); - if (ofIdx != std::string::npos) { - size_t iEnd = ofIdx, iStart = iEnd; - while (iStart > 0 && std::isdigit((unsigned char)line[iStart - 1])) iStart--; - size_t mStart = ofIdx + 4, mEnd = mStart; - while (mEnd < line.size() && std::isdigit((unsigned char)line[mEnd])) mEnd++; - if (iStart < iEnd && mStart < mEnd) { - try { - float i = std::stof(line.substr(iStart, iEnd - iStart)); - float m = std::stof(line.substr(mStart, mEnd - mStart)); - if (m > 0.0f) witnessPct = (i / m) * 100.0f; - } catch (...) {} - } - } lastStatus = line; } } @@ -861,17 +850,41 @@ void App::update() state_.sync.building_witnesses = false; state_.sync.witness_progress = 0.0f; state_.sync.witness_remaining = 0; + witness_rebuild_total_blocks_ = 0; } else if (foundWitness) { // Witness rebuild is the tail phase of a rescan — keep rescanning set so // the broader gating holds, but surface the witness-specific progress. state_.sync.rescanning = true; rescan_confirmed_active_ = true; - state_.sync.building_witnesses = true; - if (witnessPct >= 0.0f) { - state_.sync.witness_progress = witnessPct / 100.0f; + // First witness line of a phase → reset the per-phase tracking so a new + // rebuild starts from 0 rather than inheriting the previous phase's peak. + if (!state_.sync.building_witnesses) { + state_.sync.building_witnesses = true; + state_.sync.witness_progress = 0.0f; + state_.sync.witness_remaining = 0; + witness_rebuild_total_blocks_ = 0; } if (witnessRemaining >= 0) { state_.sync.witness_remaining = witnessRemaining; + // The peak "remaining" is the full span of the longest pass; derive a + // stable overall % from it. Per-call resets only shrink "remaining" + // relative to this peak, so the bar advances and never jumps back. + if (witnessRemaining > witness_rebuild_total_blocks_) + witness_rebuild_total_blocks_ = witnessRemaining; + if (witness_rebuild_total_blocks_ > 0) { + float p = 1.0f - static_cast(witnessRemaining) / + static_cast(witness_rebuild_total_blocks_); + if (p < 0.0f) p = 0.0f; + if (p > 1.0f) p = 1.0f; + if (p > state_.sync.witness_progress) // monotonic within the phase + state_.sync.witness_progress = p; + } + } else if (witnessPct >= 0.0f) { + // No "remaining" token (e.g. the initial per-tx pass) — fall back to the + // raw fraction, still clamped monotonic so it can't regress. + float p = witnessPct / 100.0f; + if (p > state_.sync.witness_progress) + state_.sync.witness_progress = p; } if (!status.empty()) { state_.sync.rescan_status = status; diff --git a/src/app.h b/src/app.h index 926f579..002e3ba 100644 --- a/src/app.h +++ b/src/app.h @@ -635,6 +635,11 @@ private: // Set when a bootstrap completes; consumed once the daemon is connected to auto-run a rescan // that reconciles the preserved wallet.dat against the freshly-imported chain. bool post_bootstrap_rescan_pending_ = false; + // Largest "blocks remaining" seen during the current witness-rebuild phase. The daemon's + // "Building Witnesses for block" fraction resets every call (it's re-invoked per connected + // block, each walking from its own start height to the tip), so we derive a stable, monotonic + // overall percentage from how far "remaining" has fallen below this peak. Reset per phase. + int witness_rebuild_total_blocks_ = 0; bool opid_poll_in_progress_ = false; // Consecutive Core-refresh cycles where BOTH core RPCs failed → likely a dead // connection. After kCoreFailuresBeforeDisconnect, tear down and reconnect. diff --git a/src/app_network.cpp b/src/app_network.cpp index 607f643..26d71d9 100644 --- a/src/app_network.cpp +++ b/src/app_network.cpp @@ -1162,6 +1162,7 @@ void App::refreshCoreData() state_.sync.building_witnesses = false; state_.sync.witness_progress = 0.0f; state_.sync.witness_remaining = 0; + witness_rebuild_total_blocks_ = 0; // Notes/witnesses were rebuilt — force a fresh history + balance pull. transactions_dirty_ = true; last_tx_block_height_ = -1; @@ -2596,6 +2597,7 @@ void App::runtimeRescan(int startHeight) state_.sync.building_witnesses = false; state_.sync.witness_progress = 0.0f; state_.sync.witness_remaining = 0; + witness_rebuild_total_blocks_ = 0; if (ok) { state_.sync.rescan_progress = 1.0f; transactions_dirty_ = true;