Reduce redundant RPC calls in periodic refresh cycle
- Remove getinfo from refreshMiningInfo slow path; daemon_version, protocol_version, and p2p_port are static per connection (set in onConnected). Move longestchain/notarized into refreshBalance's existing getblockchaininfo callback. - Remove refreshMiningInfo from refreshData(); it already runs on the 1-second fast_refresh_timer_ independently. - Make refreshAddresses demand-driven via addresses_dirty_ flag; only re-fetch when a new address is created or a send completes, not unconditionally every 5 seconds. - Gate refreshPeerInfo to only run when the Peers tab is active. - Skip duplicate getwalletinfo on connect; onConnected() already prefetches it for immediate lock-screen display, so suppress the redundant call in the first refreshData() cycle. Steady-state savings: ~8 fewer RPC calls per 5-second cycle (from ~12+ down to ~4-5 in the common case).
This commit is contained in:
@@ -422,6 +422,10 @@ private:
|
||||
// P4: Incremental transaction cache
|
||||
int last_tx_block_height_ = -1; // block height at last full tx fetch
|
||||
|
||||
// Dirty flags for demand-driven refresh
|
||||
bool addresses_dirty_ = true; // true → refreshAddresses() will run
|
||||
bool encryption_state_prefetched_ = false; // suppress duplicate getwalletinfo on connect
|
||||
|
||||
// First-run wizard state
|
||||
WizardPhase wizard_phase_ = WizardPhase::None;
|
||||
std::unique_ptr<util::Bootstrap> bootstrap_;
|
||||
|
||||
@@ -187,6 +187,13 @@ void App::onConnected()
|
||||
});
|
||||
}
|
||||
|
||||
// onConnected already fetched getwalletinfo — tell refreshData to skip
|
||||
// the duplicate call on the very first cycle.
|
||||
encryption_state_prefetched_ = true;
|
||||
|
||||
// Addresses are unknown on fresh connect — force a fetch
|
||||
addresses_dirty_ = true;
|
||||
|
||||
// Initial data refresh
|
||||
refreshData();
|
||||
refreshMarketData();
|
||||
@@ -211,11 +218,28 @@ void App::refreshData()
|
||||
if (refresh_in_progress_.exchange(true)) return;
|
||||
|
||||
refreshBalance();
|
||||
refreshAddresses();
|
||||
|
||||
// Addresses: only re-fetch when explicitly dirtied (new address, send, etc.)
|
||||
if (addresses_dirty_) {
|
||||
refreshAddresses();
|
||||
}
|
||||
|
||||
refreshTransactions();
|
||||
refreshMiningInfo();
|
||||
refreshPeerInfo();
|
||||
refreshWalletEncryptionState();
|
||||
|
||||
// Mining: handled by the 1-second fast_refresh_timer_ — skip here to
|
||||
// avoid queuing a redundant call every 5 seconds.
|
||||
|
||||
// Peers: only fetch when the Peers tab is visible
|
||||
if (current_page_ == ui::NavPage::Peers) {
|
||||
refreshPeerInfo();
|
||||
}
|
||||
|
||||
// Encryption state: skip if onConnected() already prefetched it
|
||||
if (encryption_state_prefetched_) {
|
||||
encryption_state_prefetched_ = false;
|
||||
} else {
|
||||
refreshWalletEncryptionState();
|
||||
}
|
||||
|
||||
// Clear the guard after all tasks are posted (they'll execute sequentially
|
||||
// on the worker thread, so the last one to finish signals completion).
|
||||
@@ -270,6 +294,13 @@ void App::refreshBalance()
|
||||
if (blockInfo.contains("verificationprogress"))
|
||||
state_.sync.verification_progress = blockInfo["verificationprogress"].get<double>();
|
||||
state_.sync.syncing = (state_.sync.blocks < state_.sync.headers - 2);
|
||||
|
||||
// Consolidate chain-tip fields that were previously fetched
|
||||
// via a separate getinfo call in refreshMiningInfo.
|
||||
if (blockInfo.contains("longestchain"))
|
||||
state_.longestchain = blockInfo["longestchain"].get<int>();
|
||||
if (blockInfo.contains("notarized"))
|
||||
state_.notarized = blockInfo["notarized"].get<int>();
|
||||
}
|
||||
|
||||
// Auto-shield transparent funds if enabled
|
||||
@@ -391,6 +422,8 @@ void App::refreshAddresses()
|
||||
state_.t_addresses = std::move(tAddrs);
|
||||
// P8: single rebuild via dirty flag (drains in update())
|
||||
address_list_dirty_ = true;
|
||||
// Addresses fetched successfully — clear the demand flag
|
||||
addresses_dirty_ = false;
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -583,13 +616,16 @@ void App::refreshMiningInfo()
|
||||
daemonMemMb = embedded_daemon_->getMemoryUsageMB();
|
||||
}
|
||||
|
||||
// Slow-tick counter: run full getmininginfo + getinfo every ~5 seconds
|
||||
// Slow-tick counter: run full getmininginfo every ~5 seconds
|
||||
// to reduce RPC overhead. getlocalsolps (returns H/s for RandomX) runs every tick (1s).
|
||||
// NOTE: getinfo is NOT called here — longestchain/notarized are updated by
|
||||
// refreshBalance (via getblockchaininfo), and daemon_version/protocol_version/
|
||||
// p2p_port are static for the lifetime of a connection (set in onConnected).
|
||||
bool doSlowRefresh = (mining_slow_counter_++ % 5 == 0);
|
||||
|
||||
worker_->post([this, daemonMemMb, doSlowRefresh]() -> rpc::RPCWorker::MainCb {
|
||||
json miningInfo, localHashrateJson, nodeInfo;
|
||||
bool miningOk = false, hashrateOk = false, nodeOk = false;
|
||||
json miningInfo, localHashrateJson;
|
||||
bool miningOk = false, hashrateOk = false;
|
||||
|
||||
// Fast path: only getlocalsolps (single RPC call, ~1ms) — returns H/s (RandomX)
|
||||
try {
|
||||
@@ -599,7 +635,7 @@ void App::refreshMiningInfo()
|
||||
DEBUG_LOGF("getLocalHashrate error: %s\n", e.what());
|
||||
}
|
||||
|
||||
// Slow path: getmininginfo + getinfo every ~5s
|
||||
// Slow path: getmininginfo every ~5s
|
||||
if (doSlowRefresh) {
|
||||
try {
|
||||
miningInfo = rpc_->call("getmininginfo");
|
||||
@@ -607,16 +643,9 @@ void App::refreshMiningInfo()
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("getMiningInfo error: %s\n", e.what());
|
||||
}
|
||||
|
||||
try {
|
||||
nodeInfo = rpc_->call("getinfo");
|
||||
nodeOk = true;
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("getInfo error: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
return [this, miningInfo, localHashrateJson, nodeInfo, miningOk, hashrateOk, nodeOk, daemonMemMb]() {
|
||||
return [this, miningInfo, localHashrateJson, miningOk, hashrateOk, daemonMemMb]() {
|
||||
try {
|
||||
if (hashrateOk) {
|
||||
state_.mining.localHashrate = localHashrateJson.get<double>();
|
||||
@@ -640,18 +669,6 @@ void App::refreshMiningInfo()
|
||||
state_.mining.chain = miningInfo["chain"].get<std::string>();
|
||||
state_.last_mining_update = std::time(nullptr);
|
||||
}
|
||||
if (nodeOk) {
|
||||
if (nodeInfo.contains("version"))
|
||||
state_.daemon_version = nodeInfo["version"].get<int>();
|
||||
if (nodeInfo.contains("protocolversion"))
|
||||
state_.protocol_version = nodeInfo["protocolversion"].get<int>();
|
||||
if (nodeInfo.contains("p2pport"))
|
||||
state_.p2p_port = nodeInfo["p2pport"].get<int>();
|
||||
if (nodeInfo.contains("longestchain"))
|
||||
state_.longestchain = nodeInfo["longestchain"].get<int>();
|
||||
if (nodeInfo.contains("notarized"))
|
||||
state_.notarized = nodeInfo["notarized"].get<int>();
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("[refreshMiningInfo] callback error: %s\n", e.what());
|
||||
}
|
||||
@@ -965,6 +982,7 @@ void App::createNewZAddress(std::function<void(const std::string&)> callback)
|
||||
|
||||
rpc_->z_getNewAddress([this, callback](const json& result) {
|
||||
std::string addr = result.get<std::string>();
|
||||
addresses_dirty_ = true;
|
||||
refreshAddresses();
|
||||
if (callback) callback(addr);
|
||||
});
|
||||
@@ -976,6 +994,7 @@ void App::createNewTAddress(std::function<void(const std::string&)> callback)
|
||||
|
||||
rpc_->getNewAddress([this, callback](const json& result) {
|
||||
std::string addr = result.get<std::string>();
|
||||
addresses_dirty_ = true;
|
||||
refreshAddresses();
|
||||
if (callback) callback(addr);
|
||||
});
|
||||
@@ -1206,7 +1225,11 @@ void App::sendTransaction(const std::string& from, const std::string& to,
|
||||
} catch (const std::exception& e) {
|
||||
result_str = e.what();
|
||||
}
|
||||
return [callback, ok, result_str]() {
|
||||
return [this, callback, ok, result_str]() {
|
||||
if (ok) {
|
||||
// A send changes address balances — refresh on next cycle
|
||||
addresses_dirty_ = true;
|
||||
}
|
||||
if (callback) callback(ok, result_str);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -46,6 +46,12 @@ static bool s_pool_settings_dirty = false;
|
||||
static bool s_pool_state_loaded = false;
|
||||
static bool s_show_pool_log = false; // Toggle: false=chart, true=log
|
||||
|
||||
// Chart smooth-scroll state
|
||||
static size_t s_chart_last_n = 0;
|
||||
static double s_chart_last_newest = -1.0;
|
||||
static double s_chart_update_time = 0.0;
|
||||
static float s_chart_interval = 1.0f; // measured seconds between data updates
|
||||
|
||||
// Get max threads based on hardware
|
||||
static int GetMaxMiningThreads()
|
||||
{
|
||||
@@ -983,17 +989,69 @@ void RenderMiningTab(App* app)
|
||||
float plotW = plotRight - plotLeft;
|
||||
float plotH = std::max(1.0f, plotBottom - plotTop);
|
||||
|
||||
// --- Smooth scroll: detect new data and measure interval ---
|
||||
size_t n = chartHistory.size();
|
||||
std::vector<ImVec2> points(n);
|
||||
double newestVal = chartHistory.back();
|
||||
double nowTime = ImGui::GetTime();
|
||||
bool dataChanged = (n != s_chart_last_n) || (newestVal != s_chart_last_newest);
|
||||
if (dataChanged) {
|
||||
float dt = (float)(nowTime - s_chart_update_time);
|
||||
if (dt > 0.3f && dt < 10.0f)
|
||||
s_chart_interval = s_chart_interval * 0.6f + dt * 0.4f; // smoothed
|
||||
s_chart_last_n = n;
|
||||
s_chart_last_newest = newestVal;
|
||||
s_chart_update_time = nowTime;
|
||||
}
|
||||
float elapsed = (float)(nowTime - s_chart_update_time);
|
||||
float scrollFrac = std::clamp(elapsed / s_chart_interval, 0.0f, 1.0f);
|
||||
|
||||
// Build raw data points with smooth scroll offset.
|
||||
// Newest point is anchored at plotRight; as scrollFrac grows
|
||||
// the spacing compresses by one virtual slot so the next
|
||||
// incoming point will appear seamlessly at plotRight.
|
||||
float virtualSlots = (float)(n - 1) + scrollFrac;
|
||||
if (virtualSlots < 1.0f) virtualSlots = 1.0f;
|
||||
float stepW = plotW / virtualSlots;
|
||||
|
||||
std::vector<ImVec2> rawPts(n);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
float t2 = (n > 1) ? (float)i / (float)(n - 1) : 0.0f;
|
||||
float x = plotLeft + t2 * plotW;
|
||||
float x = plotRight - (float)(n - 1 - i) * stepW;
|
||||
float y = plotBottom - (float)((chartHistory[i] - yMin) / (yMax - yMin)) * plotH;
|
||||
points[i] = ImVec2(x, y);
|
||||
rawPts[i] = ImVec2(x, y);
|
||||
}
|
||||
|
||||
// Catmull-Rom spline interpolation for smooth curve
|
||||
std::vector<ImVec2> points;
|
||||
if (n <= 2) {
|
||||
points = rawPts;
|
||||
} else {
|
||||
const int subdivs = 8; // segments between each pair of data points
|
||||
points.reserve((n - 1) * subdivs + 1);
|
||||
for (size_t i = 0; i + 1 < n; i++) {
|
||||
// Four control points: p0, p1, p2, p3
|
||||
ImVec2 p0 = rawPts[i > 0 ? i - 1 : 0];
|
||||
ImVec2 p1 = rawPts[i];
|
||||
ImVec2 p2 = rawPts[i + 1];
|
||||
ImVec2 p3 = rawPts[i + 2 < n ? i + 2 : n - 1];
|
||||
for (int s = 0; s < subdivs; s++) {
|
||||
float t = (float)s / (float)subdivs;
|
||||
float t2 = t * t;
|
||||
float t3 = t2 * t;
|
||||
// Catmull-Rom basis
|
||||
float q0 = -t3 + 2.0f * t2 - t;
|
||||
float q1 = 3.0f * t3 - 5.0f * t2 + 2.0f;
|
||||
float q2 = -3.0f * t3 + 4.0f * t2 + t;
|
||||
float q3 = t3 - t2;
|
||||
float sx = 0.5f * (p0.x * q0 + p1.x * q1 + p2.x * q2 + p3.x * q3);
|
||||
float sy = 0.5f * (p0.y * q0 + p1.y * q1 + p2.y * q2 + p3.y * q3);
|
||||
points.push_back(ImVec2(sx, sy));
|
||||
}
|
||||
}
|
||||
points.push_back(rawPts[n - 1]); // final point
|
||||
}
|
||||
|
||||
// Fill under curve
|
||||
for (size_t i = 0; i + 1 < n; i++) {
|
||||
for (size_t i = 0; i + 1 < points.size(); i++) {
|
||||
ImVec2 quad[4] = {
|
||||
points[i], points[i + 1],
|
||||
ImVec2(points[i + 1].x, plotBottom),
|
||||
@@ -1033,14 +1091,27 @@ void RenderMiningTab(App* app)
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// EARNINGS — Horizontal row card (Today | All Time | Est. Daily)
|
||||
// EARNINGS — Horizontal row card (Today | Yesterday | All Time | Est. Daily)
|
||||
// ================================================================
|
||||
{
|
||||
// Gather mining transactions from state
|
||||
double minedToday = 0.0, minedAllTime = 0.0;
|
||||
int minedTodayCount = 0, minedAllTimeCount = 0;
|
||||
double minedToday = 0.0, minedYesterday = 0.0, minedAllTime = 0.0;
|
||||
int minedTodayCount = 0, minedYesterdayCount = 0, minedAllTimeCount = 0;
|
||||
int64_t now = (int64_t)std::time(nullptr);
|
||||
int64_t dayStart = now - 86400;
|
||||
|
||||
// Calendar-day boundaries (local time)
|
||||
time_t nowT = (time_t)now;
|
||||
struct tm local;
|
||||
#ifdef _WIN32
|
||||
localtime_s(&local, &nowT);
|
||||
#else
|
||||
localtime_r(&nowT, &local);
|
||||
#endif
|
||||
local.tm_hour = 0;
|
||||
local.tm_min = 0;
|
||||
local.tm_sec = 0;
|
||||
int64_t todayStart = (int64_t)mktime(&local);
|
||||
int64_t yesterdayStart = todayStart - 86400;
|
||||
|
||||
struct MinedTx {
|
||||
int64_t timestamp;
|
||||
@@ -1055,9 +1126,12 @@ void RenderMiningTab(App* app)
|
||||
double amt = std::abs(tx.amount);
|
||||
minedAllTime += amt;
|
||||
minedAllTimeCount++;
|
||||
if (tx.timestamp >= dayStart) {
|
||||
if (tx.timestamp >= todayStart) {
|
||||
minedToday += amt;
|
||||
minedTodayCount++;
|
||||
} else if (tx.timestamp >= yesterdayStart) {
|
||||
minedYesterday += amt;
|
||||
minedYesterdayCount++;
|
||||
}
|
||||
if (recentMined.size() < 4) {
|
||||
recentMined.push_back({tx.timestamp, amt, tx.confirmations, tx.confirmations >= 100});
|
||||
@@ -1089,7 +1163,8 @@ void RenderMiningTab(App* app)
|
||||
|
||||
// === Earnings section (top of combined card) ===
|
||||
{
|
||||
float colW = (availWidth - pad * 2) / 3.0f;
|
||||
const int numCols = 4;
|
||||
float colW = (availWidth - pad * 2) / (float)numCols;
|
||||
float ey = cardMin.y + pad * 0.5f;
|
||||
char valBuf[64], subBuf2[64];
|
||||
|
||||
@@ -1106,6 +1181,10 @@ void RenderMiningTab(App* app)
|
||||
strncpy(todayVal, valBuf, sizeof(todayVal));
|
||||
strncpy(todaySub, subBuf2, sizeof(todaySub));
|
||||
|
||||
char yesterdayVal[64], yesterdaySub[64];
|
||||
snprintf(yesterdayVal, sizeof(yesterdayVal), "+%.4f", minedYesterday);
|
||||
snprintf(yesterdaySub, sizeof(yesterdaySub), "(%d blk)", minedYesterdayCount);
|
||||
|
||||
char allVal[64], allSub[64];
|
||||
snprintf(allVal, sizeof(allVal), "+%.4f", minedAllTime);
|
||||
snprintf(allSub, sizeof(allSub), "(%d blk)", minedAllTimeCount);
|
||||
@@ -1117,12 +1196,13 @@ void RenderMiningTab(App* app)
|
||||
snprintf(estVal, sizeof(estVal), "N/A");
|
||||
|
||||
EarningsEntry entries[] = {
|
||||
{ "TODAY", todayVal, todaySub, greenCol2 },
|
||||
{ "ALL TIME", allVal, allSub, OnSurface() },
|
||||
{ "EST. DAILY", estVal, nullptr, estActive ? greenCol2 : OnSurfaceDisabled() },
|
||||
{ "TODAY", todayVal, todaySub, greenCol2 },
|
||||
{ "YESTERDAY", yesterdayVal, yesterdaySub, OnSurface() },
|
||||
{ "ALL TIME", allVal, allSub, OnSurface() },
|
||||
{ "EST. DAILY", estVal, nullptr, estActive ? greenCol2 : OnSurfaceDisabled() },
|
||||
};
|
||||
|
||||
for (int ei = 0; ei < 3; ei++) {
|
||||
for (int ei = 0; ei < numCols; ei++) {
|
||||
float sx = cardMin.x + pad + ei * colW;
|
||||
float centerX = sx + colW * 0.5f;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user