fix(shutdown): don't warn "node is rebuilding" for routine witness activity / on v1.3.0
The shutdown guard fired its "Node is rebuilding its witness cache" prompt almost always on an active wallet: the daemon does a per-tx VerifyAndSetInitialWitness as each newly received wallet tx lands during normal sync, and those markers (sparse — minutes apart — with no matching "rebuilt N note witness cache(s) … in Xms") kept the heuristic latched "active". - daemonWitnessRebuildActive() now requires the last progress marker to be part of the CURRENT log activity (within ~15s of the newest log line, via a same-log timestamp delta), so routine minutes-old per-tx witness sets no longer count as an ongoing rebuild. - shouldConfirmDaemonStop() suppresses the prompt entirely on v1.3.0+ daemons (version >= 1030000): they checkpoint witness-rescan progress, so stopping mid-rebuild resumes on the next start rather than redoing it — the warning's "restarts it (several minutes)" premise no longer holds. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
41
src/app.cpp
41
src/app.cpp
@@ -5808,26 +5808,47 @@ std::vector<std::string> App::tailDaemonDebugLog(int maxLines) const
|
||||
return lines;
|
||||
}
|
||||
|
||||
// Parse a "YYYY-MM-DD HH:MM:SS ..." debug.log line prefix to time_t. Interpreted as local time, but it
|
||||
// is only ever used for DELTAS between two lines of the SAME log, so the timezone cancels. Returns 0 if
|
||||
// the line has no such timestamp prefix.
|
||||
static std::time_t parseDaemonLogTimestamp(const std::string& line)
|
||||
{
|
||||
int y = 0, mo = 0, d = 0, h = 0, mi = 0, s = 0;
|
||||
if (std::sscanf(line.c_str(), "%d-%d-%d %d:%d:%d", &y, &mo, &d, &h, &mi, &s) != 6) return 0;
|
||||
std::tm tm{};
|
||||
tm.tm_year = y - 1900; tm.tm_mon = mo - 1; tm.tm_mday = d;
|
||||
tm.tm_hour = h; tm.tm_min = mi; tm.tm_sec = s; tm.tm_isdst = -1;
|
||||
return std::mktime(&tm);
|
||||
}
|
||||
|
||||
bool App::daemonWitnessRebuildActive() const
|
||||
{
|
||||
// Scan the debug.log tail for the daemon's witness-rebuild markers (wallet.cpp): "Cleared witness
|
||||
// data from" (start), "Setting Initial Sapling Witness" / "Reading blocks for witness rebuild"
|
||||
// (progress), vs. "rebuilt N note witness cache(s)" / "aborting…" (finished). Active iff the most
|
||||
// recent relevant line is a start/progress line, not a completion.
|
||||
const auto lines = tailDaemonDebugLog(80);
|
||||
int state = 0; // 0 none, 1 active, 2 finished/aborted
|
||||
// data from" (start), "Reading blocks for witness rebuild" / "Setting Initial Sapling Witness"
|
||||
// (progress), vs. "rebuilt N note witness cache(s)" / "aborting…" (finished).
|
||||
//
|
||||
// A genuine ongoing rebuild logs progress CONTINUOUSLY. The routine per-tx witness set the daemon
|
||||
// does as each new wallet tx lands during normal sync is SPARSE (minutes apart) and must NOT trip
|
||||
// this — that was firing the "node is rebuilding" prompt on wallets that just receive frequently.
|
||||
// So require the last progress marker to be (a) later than any completion AND (b) part of the CURRENT
|
||||
// activity — within a few seconds of the newest log line (same-log timestamp delta → timezone-free).
|
||||
const auto lines = tailDaemonDebugLog(120);
|
||||
std::time_t newest = 0, lastProgress = 0, lastDone = 0;
|
||||
for (const auto& l : lines) {
|
||||
const std::time_t ts = parseDaemonLogTimestamp(l);
|
||||
if (ts > newest) newest = ts;
|
||||
if (l.find("note witness cache(s) to height") != std::string::npos ||
|
||||
l.find("aborting witness rebuild") != std::string::npos ||
|
||||
l.find("aborted during witness rebuild") != std::string::npos) {
|
||||
state = 2;
|
||||
if (ts > lastDone) lastDone = ts;
|
||||
} else if (l.find("Reading blocks for witness rebuild") != std::string::npos ||
|
||||
l.find("Setting Initial Sapling Witness") != std::string::npos ||
|
||||
l.find("Cleared witness data from") != std::string::npos) {
|
||||
state = 1;
|
||||
if (ts > lastProgress) lastProgress = ts;
|
||||
}
|
||||
}
|
||||
return state == 1;
|
||||
if (lastProgress == 0 || lastDone >= lastProgress || newest == 0) return false;
|
||||
return (newest - lastProgress) <= 15; // progress is part of the current activity → ongoing rebuild
|
||||
}
|
||||
|
||||
bool App::shouldConfirmDaemonStop() const
|
||||
@@ -5839,6 +5860,10 @@ bool App::shouldConfirmDaemonStop() const
|
||||
settings_ && settings_->getKeepDaemonRunning(),
|
||||
settings_ && settings_->getStopExternalDaemon());
|
||||
if (decision.action != daemon::DaemonController::ShutdownAction::StopDaemon) return false;
|
||||
// v1.3.0+ checkpoints witness-rescan progress, so stopping mid-rebuild resumes on the next start
|
||||
// instead of redoing it from scratch — the warning's premise no longer holds, so don't prompt.
|
||||
// (daemon_version encodes major*1e6 + minor*1e4 + rev*100 + build; v1.3.0 == 1030000.)
|
||||
if (state_.daemon_version >= 1030000) return false;
|
||||
return daemonWitnessRebuildActive();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user