diff --git a/src/ui/windows/explorer_tab.cpp b/src/ui/windows/explorer_tab.cpp index b9508c2..758c151 100644 --- a/src/ui/windows/explorer_tab.cpp +++ b/src/ui/windows/explorer_tab.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace dragonx { namespace ui { @@ -73,6 +74,10 @@ static std::string s_fuzzy_query_cached; static std::vector s_fuzzy_results; static constexpr int kMaxFuzzyResults = 100; static std::atomic s_pending_block_fetches{0}; +// Backoff for failing recent-block fetches (main thread only): consecutive failed fetches double the +// wait (2s..60s) before any new fetch is scheduled, so a failing height is not refetched every frame. +static int s_block_fetch_failures = 0; +static std::chrono::steady_clock::time_point s_block_fetch_retry_after{}; static bool s_recent_disk_cache_validation_pending = false; // Block detail modal @@ -277,6 +282,7 @@ static bool fetchRecentBlock(App* app, int height) { auto* rpc = app->rpc(); if (height < 1 || !worker || !rpc) return false; if (s_pending_block_heights.find(height) != s_pending_block_heights.end()) return false; + if (std::chrono::steady_clock::now() < s_block_fetch_retry_after) return false; s_pending_block_heights.insert(height); s_pending_block_fetches.fetch_add(1); @@ -285,9 +291,17 @@ static bool fetchRecentBlock(App* app, int height) { BlockSummary block; try { rpc::RPCClient::TraceScope trace("Explorer tab / Recent blocks"); - auto hashResult = rpc->call("getblockhash", {height}); - auto hash = hashResult.get(); - auto result = rpc->call("getblock", {hash}); + json result; + try { + // One round trip: getblock accepts a height string directly. + result = rpc->call("getblock", {std::to_string(height)}); + } catch (const rpc::RpcError& e) { + // Older daemons treat a short string as a hash and answer -5 (not found); fall back to + // getblockhash + getblock. Any other error (e.g. -8 out of range) is a real answer. + if (e.code != -5) throw; + auto hash = rpc->call("getblockhash", {height}).get(); + result = rpc->call("getblock", {hash}); + } block.height = result.value("height", 0); block.hash = result.value("hash", ""); block.time = result.value("time", (int64_t)0); @@ -300,6 +314,12 @@ static bool fetchRecentBlock(App* app, int height) { return [height, block = std::move(block)]() mutable { if (block.height > 0) { s_recent_block_cache.storeBlock(block); + s_block_fetch_failures = 0; + } else { + // A height briefly above the daemon tip (reorg) lands here too; it self-heals after the wait. + s_block_fetch_failures = std::min(s_block_fetch_failures + 1, 5); + s_block_fetch_retry_after = std::chrono::steady_clock::now() + + std::chrono::seconds(std::min(60, 1 << s_block_fetch_failures)); } s_pending_block_heights.erase(height); if (s_pending_block_fetches.load() > 0) {