- Sync ETA: a new pure util::SyncRateEstimator (App-owned) replaces the function-local statics that went stale after a rescan/restart (producing the "412h" ETA). It self-resets on a height drop and on connect/disconnect/rescan/ restart (via an atomic flag, since onConnected can run off-thread), formats with days, and uses the Warning() token. - Settings: the Explorer-tab URL fields + custom-fees/fetch-prices checkboxes now persist on edit — they were the only controls still relying on the About-only Save button that a user on this tab never sees. - Export-all-keys: hidden in the lite build (which has its own key export), instead of dead-ending on "Not connected to the daemon". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
65 lines
2.6 KiB
C++
65 lines
2.6 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
//
|
|
// A tiny, pure block-sync rate estimator for the status-bar / peers-card ETA. Pure (no ImGui, no App):
|
|
// App owns one instance, feeds it the current height + a monotonic clock once per frame while syncing,
|
|
// and resets it on connect / disconnect / rescan / restart so a height drop never produces a stale or
|
|
// absurd ("412h") ETA.
|
|
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdio>
|
|
|
|
namespace dragonx {
|
|
namespace util {
|
|
|
|
class SyncRateEstimator {
|
|
public:
|
|
void reset() { prevBlocks_ = -1; prevTime_ = 0.0; rate_ = 0.0; }
|
|
|
|
// Call once per frame while syncing. `now` is monotonic seconds (e.g. ImGui::GetTime()).
|
|
void update(int blocks, double now) {
|
|
// First sample, a rescan/restart height drop, or a clock reset → re-baseline, don't compute.
|
|
if (prevBlocks_ < 0 || blocks < prevBlocks_ || now < prevTime_) {
|
|
prevBlocks_ = blocks; prevTime_ = now; rate_ = 0.0; return;
|
|
}
|
|
const double dt = now - prevTime_;
|
|
if (blocks > prevBlocks_) {
|
|
if (dt > 0.5) {
|
|
const double raw = (blocks - prevBlocks_) / dt;
|
|
rate_ = rate_ > 0.0 ? rate_ * 0.7 + raw * 0.3 : raw; // smooth (same alpha as the old code)
|
|
prevBlocks_ = blocks; prevTime_ = now;
|
|
}
|
|
} else if (dt > 10.0) { // stalled: decay and re-baseline the clock
|
|
rate_ *= 0.5; prevTime_ = now;
|
|
if (rate_ < 0.1) rate_ = 0.0;
|
|
}
|
|
}
|
|
|
|
double rate() const { return rate_; } // blocks/sec, 0 when unknown
|
|
|
|
// <= 0 when unknown. Capped at ~1 year so a tiny rate can't overflow.
|
|
long long etaSeconds(int blocksLeft) const {
|
|
if (rate_ <= 0.1 || blocksLeft <= 0) return -1;
|
|
const double s = blocksLeft / rate_;
|
|
return s > 3.15e7 ? 31500000LL : static_cast<long long>(s);
|
|
}
|
|
|
|
private:
|
|
int prevBlocks_ = -1; // -1 = unset (distinct from a legitimate height 0)
|
|
double prevTime_ = 0.0;
|
|
double rate_ = 0.0;
|
|
};
|
|
|
|
// Human ETA with days, e.g. "3d 4h", "5h 12m", "7m 9s", "42s". English unit letters (as the old code).
|
|
inline void formatEta(long long s, char* out, std::size_t n) {
|
|
if (s >= 86400) std::snprintf(out, n, "%lldd %lldh", s / 86400, (s % 86400) / 3600);
|
|
else if (s >= 3600) std::snprintf(out, n, "%lldh %lldm", s / 3600, (s % 3600) / 60);
|
|
else if (s >= 60) std::snprintf(out, n, "%lldm %llds", s / 60, s % 60);
|
|
else std::snprintf(out, n, "%llds", s);
|
|
}
|
|
|
|
} // namespace util
|
|
} // namespace dragonx
|